From 5803536ced962be465bd5ee0b2de664bf705311a Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 15:48:00 +0000 Subject: [PATCH 1/48] Undo ordered dict code in prep for libmodulemd wrapper Revert "Preserve modulemd field ordering in dumps/dumps_all" This reverts commit 362ffeb159728d72000f9eb2b29a9c6844097374. Revert "Add internal method to dump into an OrderedDict" This reverts commit 1a7b656e87b168dda74ceecc88ef81ca8ecb55f7. --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 1d2c31e..86d43f1 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -40,7 +40,6 @@ Example usage: mmd.dump("out.yaml") """ -from collections import OrderedDict import sys import datetime import dateutil.parser @@ -61,22 +60,6 @@ from modulemd.profile import ModuleProfile supported_mdversions = ( 1, ) -# From https://stackoverflow.com/a/16782282 -# Enable yaml handling of OrderedDict, rather than serialize -# dict values alphabetically -def _represent_ordereddict(dumper, data): - value = [] - - for item_key, item_value in data.items(): - node_key = dumper.represent_data(item_key) - node_value = dumper.represent_data(item_value) - - value.append((node_key, node_value)) - - return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value) -yaml.representer.SafeRepresenter.add_representer(OrderedDict, - _represent_ordereddict) - def load_all(f): """Loads a metadata file containing multiple modulemd documents into a list of ModuleMetadata instances. @@ -115,8 +98,7 @@ def dumps_all(l): :param list l: List of ModuleMetadata instances """ - return yaml.safe_dump_all([x._dumpd_ordered() for x in l], - explicit_start=True) + return yaml.safe_dump_all([x.dumpd() for x in l], explicit_start=True) class ModuleMetadata(object): """Class representing the whole module.""" @@ -359,17 +341,17 @@ class ModuleMetadata(object): with open(f, "w") as outfile: outfile.write(data) - def _dumpd_ordered(self): - """Dumps the metadata into a OrderedDict. + def dumpd(self): + """Dumps the metadata into a dictionary. - :rtype: collections.OrderedDict + :rtype: dict """ - doc = OrderedDict() + doc = dict() # header doc["document"] = "modulemd" doc["version"] = self.mdversion # data - d = OrderedDict() + d = dict() if self.name: d["name"] = self.name if self.stream: @@ -384,18 +366,18 @@ class ModuleMetadata(object): d["description"] = self.description if self.eol: d["eol"] = str(self.eol) - d["license"] = OrderedDict() + d["license"] = dict() d["license"]["module"] = sorted(list(self.module_licenses)) if self.content_licenses: d["license"]["content"] = sorted(list(self.content_licenses)) if self.buildrequires or self.requires: - d["dependencies"] = OrderedDict() + d["dependencies"] = dict() if self.buildrequires: d["dependencies"]["buildrequires"] = self.buildrequires if self.requires: d["dependencies"]["requires"] = self.requires if self.community or self.documentation or self.tracker: - d["references"] = OrderedDict() + d["references"] = dict() if self.community: d["references"]["community"] = self.community if self.documentation: @@ -405,39 +387,39 @@ class ModuleMetadata(object): if self.xmd: d["xmd"] = self.xmd if self.profiles: - d["profiles"] = OrderedDict() + d["profiles"] = dict() for profile in self.profiles.keys(): if self.profiles[profile].description: if profile not in d["profiles"]: - d["profiles"][profile] = OrderedDict() + d["profiles"][profile] = dict() d["profiles"][profile]["description"] = \ str(self.profiles[profile].description) if self.profiles[profile].rpms: if profile not in d["profiles"]: - d["profiles"][profile] = OrderedDict() + d["profiles"][profile] = dict() d["profiles"][profile]["rpms"] = \ sorted(list(self.profiles[profile].rpms)) if self.api: - d["api"] = OrderedDict() + d["api"] = dict() if self.api.rpms: d["api"]["rpms"] = sorted(list(self.api.rpms)) if self.filter: - d["filter"] = OrderedDict() + d["filter"] = dict() if self.filter.rpms: d["filter"]["rpms"] = sorted(list(self.filter.rpms)) if self.buildopts: - d["buildopts"] = OrderedDict() + d["buildopts"] = dict() if self.buildopts.rpms: - d["buildopts"]["rpms"] = OrderedDict() + d["buildopts"]["rpms"] = dict() if self.buildopts.rpms.macros: d["buildopts"]["rpms"]["macros"] = \ self.buildopts.rpms.macros if self.components: - d["components"] = OrderedDict() + d["components"] = dict() if self.components.rpms: - d["components"]["rpms"] = OrderedDict() + d["components"]["rpms"] = dict() for p in self.components.rpms.values(): - extra = OrderedDict() + extra = dict() extra["rationale"] = p.rationale if p.buildorder: extra["buildorder"] = p.buildorder @@ -453,9 +435,9 @@ class ModuleMetadata(object): extra["multilib"] = sorted(list(p.multilib)) d["components"]["rpms"][p.name] = extra if self.components.modules: - d["components"]["modules"] = OrderedDict() + d["components"]["modules"] = dict() for p in self.components.modules.values(): - extra = OrderedDict() + extra = dict() extra["rationale"] = p.rationale if p.buildorder: extra["buildorder"] = p.buildorder @@ -465,40 +447,18 @@ class ModuleMetadata(object): extra["ref"] = p.ref d["components"]["modules"][p.name] = extra if self.artifacts: - d["artifacts"] = OrderedDict() + d["artifacts"] = dict() if self.artifacts.rpms: d["artifacts"]["rpms"] = sorted(list(self.artifacts.rpms)) doc["data"] = d return doc - def dumpd(self): - """Dumps the metadata into a dictionary. - - :rtype: dict - """ - def _convert_ordered(orig, new): - """Recurse over a nested OrderedDict, converting each to - a dict() - """ - for key, val in orig.items(): - if not isinstance(val, OrderedDict): - new[key] = val - continue - - new[key] = dict() - _convert_ordered(val, new[key]) - - ordered = self._dumpd_ordered() - converted = dict() - _convert_ordered(ordered, converted) - return converted - def dumps(self): """Dumps the metadata into a string. :rtype: str """ - return yaml.safe_dump(self._dumpd_ordered(), default_flow_style=False) + return yaml.safe_dump(self.dumpd(), default_flow_style=False) @property def mdversion(self): From f3a41cff7accbc3ded2343b86e2c138a54c7d6b9 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:35:06 +0000 Subject: [PATCH 2/48] Bump version to 1.4.0dev --- diff --git a/setup.py b/setup.py index f1f9fd3..1bbc51b 100755 --- a/setup.py +++ b/setup.py @@ -9,9 +9,9 @@ def read(f): setup( name = "modulemd", - version = "1.3.3", - author = "Petr Šabata", - author_email = "contyk@redhat.com", + version = "1.4.0", + author = "Stephen Gallagher", + author_email = "sgallagh@redhat.com", description = ("A python library for manipulation of the proposed " "module metadata format."), license = "MIT", From b86cdee705898e7ceaf9efa9ad4ad6eb1538b82f Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:35:06 +0000 Subject: [PATCH 3/48] First pass at updating the main modulemd.ModuleMetadata --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 86d43f1..21e8e01 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -58,6 +58,11 @@ from modulemd.components.rpm import ModuleComponentRPM from modulemd.filter import ModuleFilter from modulemd.profile import ModuleProfile +import gi +from gi.repository import GLib +gi.require_version('Modulemd', '0.1') +from gi.repository import Modulemd + supported_mdversions = ( 1, ) def load_all(f): @@ -107,30 +112,44 @@ class ModuleMetadata(object): def __init__(self): """Creates a new ModuleMetadata instance.""" - self.mdversion = max(supported_mdversions) - self.name = "" - self.stream = "" - self.version = 0 - self.context = "" - self.arch = "" - self.summary = "" - self.description = "" - self.eol = None - self.module_licenses = set() - self.content_licenses = set() - self.buildrequires = dict() - self.requires = dict() - self.community = "" - self.documentation = "" - self.tracker = "" - self.xmd = dict() - self.profiles = dict() + # Under the hood, we will use the ModulemdModule type + self.module = Modulemd.Module() + + self.module.set_mdversion(max(supported_mdversions)) + self.module.set_name("") + self.module.set_stream("") + self.module.set_version(0) + self.module.set_context("") + self.module.set_arch("") + self.module.set_summary("") + self.module.set_description("") + # Don't pre-set EOL, since we don't have a valid value yet + # The Modulemd.Module() default initializes it to an appropriate invalid + # date. + # self.module.set_eol() + self.module.set_module_licenses(Modulemd.SimpleSet()) + self.module.set_content_licenses(Modulemd.SimpleSet()) + self.module.set_buildrequires({}) + self.module.set_requires({}) + self.module.set_community("") + self.module.set_documentation("") + self.module.set_tracker("") + self.module.set_xmd({}) + self.module.set_profiles({}) + self.module.set_rpm_api(Modulemd.SimpleSet()) + self.module.set_rpm_filter(Modulemd.SimpleSet()) + self.module.set_rpm_buildopts({}) + self.module.set_rpm_components({}) + self.module.set_module_components({}) + self.module.set_rpm_artifacts(Modulemd.SimpleSet()) + self.api = ModuleAPI() self.filter = ModuleFilter() self.buildopts = ModuleBuildopts() self.components = ModuleComponents() self.artifacts = ModuleArtifacts() + def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 4/48] Load and dump using libmodulemd --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 21e8e01..b66fc24 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -71,9 +71,15 @@ def load_all(f): :param str f: File name to load """ - with open(f, "r") as infile: - data = infile.read() - return loads_all(data) + l = list() + mmd_l = Modulemd.Module.new_all_from_file(f) + + for mmd in mmd_l: + m = ModuleMetadata() + m.module = mmd + l.append(m) + + return l def loads_all(s): """Loads multiple modulemd documents from a YAML multidocument @@ -82,10 +88,13 @@ def loads_all(s): :param str s: String containing multiple YAML documents. """ l = list() - for doc in yaml.safe_load_all(s): + mmd_l = Modulemd.Module.new_all_from_string(s) + + for mmd in mmd_l: m = ModuleMetadata() - m.loadd(doc) + m.module = mmd l.append(m) + return l def dump_all(f, l): @@ -94,8 +103,11 @@ def dump_all(f, l): :param str f: Output filename :param list l: List of ModuleMetadata instances """ - with open(f, "w") as outfile: - outfile.write(dumps_all(l)) + mmds = list() + for module in l: + mmds.append(module.module) + + return Modulemd.Module.dump_all(mmds, f) def dumps_all(l): """Dumps a list of ModuleMetadata instance into a YAML multidocument @@ -103,7 +115,11 @@ def dumps_all(l): :param list l: List of ModuleMetadata instances """ - return yaml.safe_dump_all([x.dumpd() for x in l], explicit_start=True) + mmds = list() + for module in l: + mmds.append(module.module) + + return Modulemd.Module.dumps_all(mmds) class ModuleMetadata(object): """Class representing the whole module.""" @@ -207,8 +223,7 @@ class ModuleMetadata(object): :param str s: Raw metadata in YAML """ - yamld = yaml.safe_load(s) - self.loadd(yamld) + self.module = Modulemd.Module.new_from_string(f) def loadd(self, d): """Loads metadata from a dictionary. @@ -216,263 +231,29 @@ class ModuleMetadata(object): :param dict d: YAML metadata parsed into a dict :raises ValueError: If the metadata is invalid or unsupported. """ - # header - if "document" not in d or d["document"] != "modulemd": - raise ValueError("The supplied data isn't a valid modulemd document") - if "version" not in d: - raise ValueError("Document version is required") - if d["version"] not in supported_mdversions: - raise ValueError("The supplied metadata version isn't supported") - self.mdversion = d["version"] - if "data" not in d or not isinstance(d["data"], dict): - raise ValueError("Data section missing or mangled") - # data - data = d["data"] - if "name" in data: - self.name = str(data["name"]) - if "stream" in data: - self.stream = str(data["stream"]) - if "version" in data: - self.version = int(data["version"]) - if "context" in data: - self.context = str(data["context"]) - if "arch" in data: - self.arch = str(data["arch"]) - if "summary" in data: - self.summary = str(data["summary"]) - if "description" in data: - self.description = str(data["description"]) - if "eol" in data: - try: - self.eol = dateutil.parser.parse(str(data["eol"])).date() - except: - self.eol = None - if ("license" in data - and isinstance(data["license"], dict) - and "module" in data["license"] - and data["license"]["module"]): - self.module_licenses = set(data["license"]["module"]) - if ("license" in data - and isinstance(data["license"], dict) - and "content" in data["license"]): - self.content_licenses = set(data["license"]["content"]) - if ("dependencies" in data - and isinstance(data["dependencies"], dict)): - if ("buildrequires" in data["dependencies"] - and isinstance(data["dependencies"]["buildrequires"], dict)): - for n, s in data["dependencies"]["buildrequires"].items(): - self.add_buildrequires(str(n), str(s)) - if ("requires" in data["dependencies"] - and isinstance(data["dependencies"]["requires"], dict)): - for n, s in data["dependencies"]["requires"].items(): - self.add_requires(str(n), str(s)) - if "references" in data and data["references"]: - if "community" in data["references"]: - self.community = data["references"]["community"] - if "documentation" in data["references"]: - self.documentation = data["references"]["documentation"] - if "tracker" in data["references"]: - self.tracker = data["references"]["tracker"] - if "xmd" in data: - self.xmd = data["xmd"] - if ("profiles" in data - and isinstance(data["profiles"], dict)): - for profile in data["profiles"].keys(): - self.profiles[profile] = ModuleProfile() - if "description" in data["profiles"][profile]: - self.profiles[profile].description = \ - str(data["profiles"][profile]["description"]) - if "rpms" in data["profiles"][profile]: - self.profiles[profile].rpms = \ - set(data["profiles"][profile]["rpms"]) - if ("api" in data - and isinstance(data["api"], dict)): - self.api = ModuleAPI() - if ("rpms" in data["api"] - and isinstance(data["api"]["rpms"],list)): - self.api.rpms = set(data["api"]["rpms"]) - if ("filter" in data - and isinstance(data["filter"], dict)): - self.filter = ModuleFilter() - if ("rpms" in data["filter"] - and isinstance(data["filter"]["rpms"],list)): - self.filter.rpms = set(data["filter"]["rpms"]) - if ("buildopts" in data - and isinstance(data["buildopts"], dict)): - self.buildopts = ModuleBuildopts() - if ("rpms" in data["buildopts"] - and isinstance(data["buildopts"]["rpms"], dict)): - self.buildopts.rpms = ModuleBuildoptsRPMs() - if ("macros" in data["buildopts"]["rpms"] - and isinstance(data["buildopts"]["rpms"]["macros"], str)): - self.buildopts.rpms.macros = data["buildopts"]["rpms"]["macros"] - if ("components" in data - and isinstance(data["components"], dict)): - self.components = ModuleComponents() - if "rpms" in data["components"]: - for p, e in data["components"]["rpms"].items(): - extras = dict() - extras["rationale"] = e["rationale"] - if "buildorder" in e: - extras["buildorder"] = int(e["buildorder"]) - if "repository" in e: - extras["repository"] = str(e["repository"]) - if "cache" in e: - extras["cache"] = str(e["cache"]) - if "ref" in e: - extras["ref"] = str(e["ref"]) - if ("arches" in e - and isinstance(e["arches"], list)): - extras["arches"] = set(str(x) for x in e["arches"]) - if ("multilib" in e - and isinstance(e["multilib"], list)): - extras["multilib"] = set(str(x) for x in e["multilib"]) - self.components.add_rpm(p, **extras) - if "modules" in data["components"]: - for p, e in data["components"]["modules"].items(): - extras = dict() - extras["rationale"] = e["rationale"] - if "buildorder" in e: - extras["buildorder"] = int(e["buildorder"]) - if "repository" in e: - extras["repository"] = str(e["repository"]) - if "ref" in e: - extras["ref"] = str(e["ref"]) - self.components.add_module(p, **extras) - if ("artifacts" in data - and isinstance(data["artifacts"], dict)): - self.artifacts = ModuleArtifacts() - if ("rpms" in data["artifacts"] - and isinstance(data["artifacts"]["rpms"],list)): - self.artifacts.rpms = set(data["artifacts"]["rpms"]) + self.loads(yaml.dump(d)) + def dump(self, f): """Dumps the metadata into the supplied file. :param str f: File name of the destination """ - data = self.dumps() - with open(f, "w") as outfile: - outfile.write(data) + self.module.dump(f) def dumpd(self): """Dumps the metadata into a dictionary. :rtype: dict """ - doc = dict() - # header - doc["document"] = "modulemd" - doc["version"] = self.mdversion - # data - d = dict() - if self.name: - d["name"] = self.name - if self.stream: - d["stream"] = self.stream - if self.version: - d["version"] = self.version - if self.context: - d["context"] = self.context - if self.arch: - d["arch"] = self.arch - d["summary"] = self.summary - d["description"] = self.description - if self.eol: - d["eol"] = str(self.eol) - d["license"] = dict() - d["license"]["module"] = sorted(list(self.module_licenses)) - if self.content_licenses: - d["license"]["content"] = sorted(list(self.content_licenses)) - if self.buildrequires or self.requires: - d["dependencies"] = dict() - if self.buildrequires: - d["dependencies"]["buildrequires"] = self.buildrequires - if self.requires: - d["dependencies"]["requires"] = self.requires - if self.community or self.documentation or self.tracker: - d["references"] = dict() - if self.community: - d["references"]["community"] = self.community - if self.documentation: - d["references"]["documentation"] = self.documentation - if self.tracker: - d["references"]["tracker"] = self.tracker - if self.xmd: - d["xmd"] = self.xmd - if self.profiles: - d["profiles"] = dict() - for profile in self.profiles.keys(): - if self.profiles[profile].description: - if profile not in d["profiles"]: - d["profiles"][profile] = dict() - d["profiles"][profile]["description"] = \ - str(self.profiles[profile].description) - if self.profiles[profile].rpms: - if profile not in d["profiles"]: - d["profiles"][profile] = dict() - d["profiles"][profile]["rpms"] = \ - sorted(list(self.profiles[profile].rpms)) - if self.api: - d["api"] = dict() - if self.api.rpms: - d["api"]["rpms"] = sorted(list(self.api.rpms)) - if self.filter: - d["filter"] = dict() - if self.filter.rpms: - d["filter"]["rpms"] = sorted(list(self.filter.rpms)) - if self.buildopts: - d["buildopts"] = dict() - if self.buildopts.rpms: - d["buildopts"]["rpms"] = dict() - if self.buildopts.rpms.macros: - d["buildopts"]["rpms"]["macros"] = \ - self.buildopts.rpms.macros - if self.components: - d["components"] = dict() - if self.components.rpms: - d["components"]["rpms"] = dict() - for p in self.components.rpms.values(): - extra = dict() - extra["rationale"] = p.rationale - if p.buildorder: - extra["buildorder"] = p.buildorder - if p.repository: - extra["repository"] = p.repository - if p.ref: - extra["ref"] = p.ref - if p.cache: - extra["cache"] = p.cache - if p.arches: - extra["arches"] = sorted(list(p.arches)) - if p.multilib: - extra["multilib"] = sorted(list(p.multilib)) - d["components"]["rpms"][p.name] = extra - if self.components.modules: - d["components"]["modules"] = dict() - for p in self.components.modules.values(): - extra = dict() - extra["rationale"] = p.rationale - if p.buildorder: - extra["buildorder"] = p.buildorder - if p.repository: - extra["repository"] = p.repository - if p.ref: - extra["ref"] = p.ref - d["components"]["modules"][p.name] = extra - if self.artifacts: - d["artifacts"] = dict() - if self.artifacts.rpms: - d["artifacts"]["rpms"] = sorted(list(self.artifacts.rpms)) - doc["data"] = d - return doc + return yaml.safe_load(self.dumps()) def dumps(self): """Dumps the metadata into a string. :rtype: str """ - return yaml.safe_dump(self.dumpd(), default_flow_style=False) + return self.module.dumps() @property def mdversion(self): From 702aa41f5830d9fc5408ffeb4b6324afeebf716b Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 5/48] Parse xmd input into GLib.Variants --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index b66fc24..4330b3c 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -613,15 +613,59 @@ class ModuleMetadata(object): d[key] = value.unpack() return d + def _variant_str(s): + if not isinstance(s, str): + raise TypeError ("Only strings are supported for scalars") + + return GLib.Variant('s', s) + + def _variant_list(l): + l_variant = list() + for item in l: + if type(item) == str: + l_variant.append(ModuleMetadata._variant_str(item)) + elif type(item) == list: + l_variant.append(ModuleMetadata._variant_list(item)) + elif type(item) == dict: + l_variant.append(ModuleMetadata._variant_dict(item)) + else: + raise TypeError ("Cannot convert unknown type") + return GLib.Variant('av', l_variant); + + def _variant_dict_values(d): + if not isinstance(d, dict): + raise TypeError ("Only dictionaries are supported for mappings") + + d_variant = dict() + for k, v in d.items(): + if type(v) == str: + d_variant[k] = ModuleMetadata._variant_str(v); + pass + elif type(v) == list: + d_variant[k] = ModuleMetadata._variant_list(v); + elif type(v) == dict: + d_variant[k] = ModuleMetadata._variant_dict(v); + else: + raise TypeError ("Cannot convert unknown type") + return d_variant + + def _variant_dict(d): + if not isinstance(d, dict): + raise TypeError ("Only dictionaries are supported for mappings") + + d_variant = ModuleMetadata._variant_dict_values(d) + + return GLib.Variant('a{sv}', d_variant); + + @xmd.setter def xmd(self, d): if not isinstance(d, dict): raise TypeError("xmd: data type not supported") - # TODO: implement this - return + xmd = ModuleMetadata._variant_dict_values(d) - self.module.set_xmd(d) + self.module.set_xmd(xmd) @property def profiles(self): From 558f32928b27f9460e8458aeae00981fad036e7a Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 6/48] Implement ModuleAPI --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 4330b3c..69ccec7 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -159,7 +159,6 @@ class ModuleMetadata(object): self.module.set_module_components({}) self.module.set_rpm_artifacts(Modulemd.SimpleSet()) - self.api = ModuleAPI() self.filter = ModuleFilter() self.buildopts = ModuleBuildopts() self.components = ModuleComponents() @@ -687,13 +686,19 @@ class ModuleMetadata(object): @property def api(self): """A ModuleAPI instance representing the module's public API.""" - return self._api + api = ModuleAPI(parent=self) + api.rpms = set(self.module.get_rpm_api().get()) + return api @api.setter def api(self, o): if not isinstance(o, ModuleAPI): raise TypeError("api: data type not supported") - self._api = o + + rpms = Modulemd.SimpleSet() + rpms.set(list(o.rpms)) + + self.module.set_rpm_api(rpms) @property def filter(self): diff --git a/modulemd/api.py b/modulemd/api.py index 41bbb90..a48d848 100644 --- a/modulemd/api.py +++ b/modulemd/api.py @@ -25,12 +25,15 @@ supported_content = ( "rpms", ) +from gi.repository import Modulemd + class ModuleAPI(object): """Class representing a particular module API.""" - def __init__(self): + def __init__(self, parent=None): """Creates a new ModuleAPI instance.""" self.rpms = set() + self.parent = parent def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 7/48] Implement ModuleFilter --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 69ccec7..b12d268 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -159,7 +159,6 @@ class ModuleMetadata(object): self.module.set_module_components({}) self.module.set_rpm_artifacts(Modulemd.SimpleSet()) - self.filter = ModuleFilter() self.buildopts = ModuleBuildopts() self.components = ModuleComponents() self.artifacts = ModuleArtifacts() @@ -703,13 +702,19 @@ class ModuleMetadata(object): @property def filter(self): """A ModuleFilter instance representing the module's filter.""" - return self._filter + filter = ModuleFilter(parent=self) + filter.rpms = set(self.module.get_rpm_filter().get()) + return filter @filter.setter def filter(self, o): if not isinstance(o, ModuleFilter): raise TypeError("filter: data type not supported") - self._filter = o + + rpms = Modulemd.SimpleSet() + rpms.set(list(o.rpms)) + + self.module.set_rpm_filter(rpms) @property def buildopts(self): diff --git a/modulemd/filter.py b/modulemd/filter.py index 232ad0d..b2cecd1 100644 --- a/modulemd/filter.py +++ b/modulemd/filter.py @@ -25,12 +25,15 @@ supported_content = ( "rpms", ) +from gi.repository import Modulemd + class ModuleFilter(object): """Class representing a particular module filter.""" - def __init__(self): + def __init__(self, parent=None): """Creates a new ModuleFilter instance.""" self.rpms = set() + self.parent = parent def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 8/48] Implement ModuleArtifacts --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index b12d268..fb4fc22 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -161,7 +161,6 @@ class ModuleMetadata(object): self.buildopts = ModuleBuildopts() self.components = ModuleComponents() - self.artifacts = ModuleArtifacts() def __repr__(self): @@ -745,10 +744,16 @@ class ModuleMetadata(object): @property def artifacts(self): """A ModuleArtifacts instance representing the module's artifacts.""" - return self._artifacts + artifacts = ModuleArtifacts(parent=self) + artifacts.rpms = set(self.module.get_rpm_artifacts().get()) + return artifacts @artifacts.setter def artifacts(self, o): if not isinstance(o, ModuleArtifacts): raise TypeError("artifacts: data type not supported") - self._artifacts = o + + rpms = Modulemd.SimpleSet() + rpms.set(list(o.rpms)) + + self.module.set_rpm_artifacts(rpms) diff --git a/modulemd/artifacts.py b/modulemd/artifacts.py index 6d52a00..efdc228 100644 --- a/modulemd/artifacts.py +++ b/modulemd/artifacts.py @@ -25,12 +25,15 @@ supported_content = ( "rpms", ) +from gi.repository import Modulemd + class ModuleArtifacts(object): """Class representing a particular module artifacts.""" - def __init__(self): + def __init__(self, parent=None): """Creates a new ModuleArtifacts instance.""" self.rpms = set() + self.parent = parent def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 9/48] restore accidentally deleted repr() calls --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index fb4fc22..d4c15de 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -206,6 +206,9 @@ class ModuleMetadata(object): repr(sorted(self.xmd)), repr(sorted(self.profiles)), repr(self.api), + repr(self.filter), + repr(self.components), + repr(self.artifacts) ) def load(self, f): From 55891dbdb03844828082e3899ea89dfa08d0999c Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 10/48] Convert licenses to Modulemd --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index d4c15de..7429e3e 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -413,11 +413,13 @@ class ModuleMetadata(object): """ if not isinstance(s, str): raise TypeError("del_module_license: data type not supported") - self._module_licenses.discard(s) + licenses = self.module.get_module_licenses() + licenses.remove(s) + self.module.set_module_licenses(licenses) def clear_module_licenses(self): """Clears the module licenses set.""" - self._module_licenses.clear() + self.module.set_module_licenses(Modulemd.SimpleSet()) @property def content_licenses(self): @@ -434,7 +436,7 @@ class ModuleMetadata(object): raise TypeError("content_licenses: data type not supported") simpleset = Modulemd.SimpleSet() simpleset.set(list(ss)) - self.module.set_module_licenses(simpleset) + self.module.set_content_licenses(simpleset) def add_content_license(self, s): """Adds a content license to the set. @@ -443,9 +445,9 @@ class ModuleMetadata(object): """ if not isinstance(s, str): raise TypeError("add_content_license: data type not supported") - simpleset = self.module.get_module_licenses() + simpleset = self.module.get_content_licenses() simpleset.add(s) - self.module.set_module_licenses(simpleset) + self.module.set_content_licenses(simpleset) def del_content_license(self, s): """Removes the supplied license from the content licenses set. @@ -454,11 +456,13 @@ class ModuleMetadata(object): """ if not isinstance(s, str): raise TypeError("del_content_license: data type not supported") - self._content_licenses.discard(s) + licenses = self.module.get_content_licenses() + licenses.remove(s) + self.module.set_content_licenses(licenses) def clear_content_licenses(self): """Clears the content licenses set.""" - self._content_licenses.clear() + self.module.set_content_licenses(Modulemd.SimpleSet()) @property def requires(self): From be9d78d9892795cede47ea2ebf0a5e58aeb43bee Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 11/48] Convert ModuleBuildopts to libmodulemd --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 7429e3e..757fbd2 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -727,6 +727,7 @@ class ModuleMetadata(object): """A ModuleBuildopts instance representing the additional module components build options. """ + self._buildopts = ModuleBuildopts(parent=self) return self._buildopts @buildopts.setter diff --git a/modulemd/buildopts/__init__.py b/modulemd/buildopts/__init__.py index 5aa7b73..9f4cc11 100644 --- a/modulemd/buildopts/__init__.py +++ b/modulemd/buildopts/__init__.py @@ -30,9 +30,11 @@ supported_content = ( "rpms", ) class ModuleBuildopts(object): """Class representing component build options.""" - def __init__(self): + def __init__(self, parent=None): """Creates a new ModuleBuildopts instance.""" - self.rpms = ModuleBuildoptsRPMs() + self.rpms = ModuleBuildoptsRPMs(parent=parent) + self.parent = parent + def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 12/48] Add support for securitylevels --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 757fbd2..3f3ee5b 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -142,7 +142,7 @@ class ModuleMetadata(object): # Don't pre-set EOL, since we don't have a valid value yet # The Modulemd.Module() default initializes it to an appropriate invalid # date. - # self.module.set_eol() + self.module.set_servicelevels({}) self.module.set_module_licenses(Modulemd.SimpleSet()) self.module.set_content_licenses(Modulemd.SimpleSet()) self.module.set_buildrequires({}) @@ -378,6 +378,16 @@ class ModuleMetadata(object): self.module.set_eol(d) @property + def servicelevels(self): + """A dictionary of service levels applying to this module""" + return self.module.get_servicelevels() + + @servicelevels.setter + def servicelevels(self, d): + if not isinstance(d, dict): + self.module.set_servicelevels(d) + + @property def module_licenses(self): """A set of strings, a property, representing the license terms of the module itself.""" diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index c420eb9..8912912 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -53,6 +53,16 @@ class TestIO(unittest.TestCase): self.assertEqual(self.mmd.summary, "An example module") self.assertEqual(self.mmd.description, "A module for the demonstration of the metadata format. Also, the obligatory lorem ipsum dolor sit amet goes right here.") self.assertEqual(self.mmd.eol, datetime.date(2077, 10, 23)) + securitylevel = self.mmd.servicelevels['security'] + securityeol = datetime.date(securitylevel.get_eol().get_year(), + securitylevel.get_eol().get_month(), + securitylevel.get_eol().get_day()) + self.assertEqual(securityeol, datetime.date(2019, 3, 30)) + featurelevel = self.mmd.servicelevels['features'] + featureeol = datetime.date(featurelevel.get_eol().get_year(), + featurelevel.get_eol().get_month(), + featurelevel.get_eol().get_day()) + self.assertEqual(featureeol, datetime.date(2018, 12, 31)) self.assertSetEqual(self.mmd.module_licenses, set(["MIT"])) self.assertSetEqual(self.mmd.content_licenses, set(["Beerware", "GPLv2+", "zlib"])) diff --git a/modulemd/tests/test_validation.py b/modulemd/tests/test_validation.py index 19abc8f..563dd2a 100644 --- a/modulemd/tests/test_validation.py +++ b/modulemd/tests/test_validation.py @@ -77,6 +77,9 @@ class TestValidation(unittest.TestCase): def test_eol_type(self): self.assertRaises(TypeError, setattr, self.mmd, "eol", 0) + def test_servicelevels_type(self): + self.assertRaises(TypeError, setattr, self.mmd, "servicelevels", 0) + def test_module_license_type(self): self.assertRaises(TypeError, setattr, self.mmd, "module_licenses", 0) diff --git a/spec.yaml b/spec.yaml index cf9b790..6b5b66b 100644 --- a/spec.yaml +++ b/spec.yaml @@ -43,6 +43,16 @@ data: # receive any more updates. Typically defined in an external data # source and filled in by the buildsystem. eol: 2077-10-23 + # Service levels, optional + # This is a dictionary of important dates (and possibly supplementary data + # in the future) that describes the end point of certain functionality, + # such as the date when the module will transition to "security fixes only" + # or go completely end-of-life + servicelevels: + security: + eol: 2019-03-30 + features: + eol: 2018-12-31 # Module and content licenses in the Fedora license identifier # format, required license: From 42c6fc60998954feb577300f1d35a749bf26a008 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 13/48] Convert ModuleProfile to libmodulemd --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 3f3ee5b..a2a390c 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -685,9 +685,11 @@ class ModuleMetadata(object): def profiles(self): """A dictionary property representing the module profiles.""" # TODO: return the profiles as a dict of ModuleProfile types - return {} + d = dict() + for k,v in self.module.get_profiles().items(): + d[k] = ModuleProfile(profile=v, parent=self, name=k) - return self._profiles + return d @profiles.setter def profiles(self, d): @@ -696,7 +698,20 @@ class ModuleMetadata(object): for k, v in d.items(): if not isinstance(k, str) or not isinstance(v, ModuleProfile): raise TypeError("profiles: data type not supported") - self._profiles = d + + profiles = self.module.get_profiles() + + for k,v in d.items(): + profile = Modulemd.Profile() + if v.description: + profile.set_description(v.description) + + ss = Modulemd.SimpleSet() + ss.set(list(v.rpms)) + + profile.set_rpms(ss) + profiles[k] = profile + self.module.set_profiles(profiles) @property def api(self): diff --git a/modulemd/profile.py b/modulemd/profile.py index 254ff76..9e2d2ad 100644 --- a/modulemd/profile.py +++ b/modulemd/profile.py @@ -25,13 +25,25 @@ supported_content = ( "rpms", ) +from gi.repository import Modulemd + class ModuleProfile(object): """Class representing a particular module profile.""" - def __init__(self): + def __init__(self, profile=None, name=None, parent=None): """Creates a new ModuleProfile instance.""" - self.description = "" - self.rpms = set() + if profile: + if isinstance(profile, Modulemd.Profile): + self.profile = profile + else: + raise TypeError("Supplied value is not a profile") + else: + self.profile = Modulemd.Profile() + + if parent: + self.parent = parent + if name: + self.name = name def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 14/48] update required libmodulemd version --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index a2a390c..c014214 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -60,7 +60,7 @@ from modulemd.profile import ModuleProfile import gi from gi.repository import GLib -gi.require_version('Modulemd', '0.1') +gi.require_version('Modulemd', '0.2') from gi.repository import Modulemd supported_mdversions = ( 1, ) From b7f27beefb94120ea715724b22f7a9bbf467f030 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 15/48] fix typo --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index c014214..957e015 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -223,7 +223,7 @@ class ModuleMetadata(object): :param str s: Raw metadata in YAML """ - self.module = Modulemd.Module.new_from_string(f) + self.module = Modulemd.Module.new_from_string(s) def loadd(self, d): """Loads metadata from a dictionary. From e27c54544e00b053f763b6eb18ad26c3de904642 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 16/48] split up monolithic test method --- diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index 8912912..0eff5db 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -35,23 +35,52 @@ sys.path.insert(0, os.path.join(DIR, "..")) from modulemd import ModuleMetadata, dump_all, load_all -class TestIO(unittest.TestCase): +class _TestIOBase(unittest.TestCase): maxDiff = None # display diff when a test fails @classmethod def setUpClass(cls): cls.mmd = ModuleMetadata() + +class TestIOMMDLoads(_TestIOBase): + def test_load_spec(self): self.mmd.load("spec.yaml") + + +class TestIO(_TestIOBase): + + @classmethod + def setUpClass(cls): + super(TestIO, cls).setUpClass() + cls.mmd.load("spec.yaml") + + def test_mdversion(self): self.assertEqual(self.mmd.mdversion, 1) + + def test_name(self): self.assertEqual(self.mmd.name, "foo") + + def test_stream(self): self.assertEqual(self.mmd.stream, "stream-name") + + def test_version(self): self.assertEqual(self.mmd.version, 20160927144203) + + def test_context(self): self.assertEqual(self.mmd.context, "c0ffee43") + + def test_arch(self): self.assertEqual(self.mmd.arch, "x86_64") + + def test_summary(self): self.assertEqual(self.mmd.summary, "An example module") + + def test_description(self): self.assertEqual(self.mmd.description, "A module for the demonstration of the metadata format. Also, the obligatory lorem ipsum dolor sit amet goes right here.") + + def test_eol(self): self.assertEqual(self.mmd.eol, datetime.date(2077, 10, 23)) securitylevel = self.mmd.servicelevels['security'] securityeol = datetime.date(securitylevel.get_eol().get_year(), @@ -63,22 +92,40 @@ class TestIO(unittest.TestCase): featurelevel.get_eol().get_month(), featurelevel.get_eol().get_day()) self.assertEqual(featureeol, datetime.date(2018, 12, 31)) + + def test_module_licenses(self): self.assertSetEqual(self.mmd.module_licenses, set(["MIT"])) + + def test_content_licenses(self): self.assertSetEqual(self.mmd.content_licenses, set(["Beerware", "GPLv2+", "zlib"])) + + def test_xmd(self): self.assertEqual(self.mmd.xmd, {'some_key': 'some_data'}) + + def test_buildrequires(self): self.assertDictEqual(self.mmd.buildrequires, { "platform" : "and-its-stream-name", "extra-build-env" : "and-its-stream-name-too" }) + + def test_requires(self): self.assertDictEqual(self.mmd.requires, { "platform" : "and-its-stream-name" }) + + def test_community(self): self.assertEqual(self.mmd.community, "http://www.example.com/") + + def test_documentation(self): self.assertEqual(self.mmd.documentation, "http://www.example.com/") + + def test_tracker(self): self.assertEqual(self.mmd.tracker, "http://www.example.com/") + + def test_profiles(self): self.assertSetEqual(set(self.mmd.profiles.keys()), set(["default", "minimal", "container", "buildroot", "srpm-buildroot"])) @@ -88,10 +135,16 @@ class TestIO(unittest.TestCase): "Minimal profile installing only the bar package.") self.assertSetEqual(self.mmd.profiles["minimal"].rpms, set(["bar"])) + + def test_api(self): self.assertSetEqual(self.mmd.api.rpms, set(["bar", "bar-extras", "bar-devel", "baz", "xxx"])) + + def test_filter(self): self.assertSetEqual(self.mmd.filter.rpms, set(["baz-nonfoo"])) + + def test_components_rpms(self): self.assertSetEqual(set(self.mmd.components.rpms.keys()), set(["bar", "baz", "xxx", "xyz"])) self.assertEqual(self.mmd.components.rpms["bar"].rationale, @@ -114,6 +167,8 @@ class TestIO(unittest.TestCase): "xyz is a bundled dependency of xxx.") self.assertEqual(self.mmd.components.rpms["xyz"].buildorder, 10) + + def test_components_modules(self): self.assertSetEqual(set(self.mmd.components.modules), set(["includedmodule"])) self.assertEqual(self.mmd.components.modules["includedmodule"].rationale, @@ -124,6 +179,8 @@ class TestIO(unittest.TestCase): "somecoolbranchname") self.assertEqual(self.mmd.components.modules["includedmodule"].buildorder, 100) + + def test_artifacts_rpms(self): self.assertSetEqual(self.mmd.artifacts.rpms, set(["bar-0:1.23-1.module_deadbeef.x86_64", "bar-devel-0:1.23-1.module_deadbeef.x86_64", @@ -132,6 +189,8 @@ class TestIO(unittest.TestCase): "xxx-0:1-1.module_deadbeef.x86_64", "xxx-0:1-1.module_deadbeef.i686", "xyz-0:1-1.module_deadbeef.x86_64"])) + + def test_buildopts_rpms_macros(self): self.assertEqual(self.mmd.buildopts.rpms.macros, "%demomacro 1\n%demomacro2 %{demomacro}23\n") From d973a4af7c15a7e40720e2190b0c1a18668c2d20 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 17/48] import gobject-introspected modules early This is so we can keep the required module version in only one place. --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 957e015..5ece126 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -48,6 +48,11 @@ import yaml if sys.version_info > (3,): long = int +import gi +from gi.repository import GLib +gi.require_version('Modulemd', '0.2') +from gi.repository import Modulemd + from modulemd.api import ModuleAPI from modulemd.artifacts import ModuleArtifacts from modulemd.buildopts import ModuleBuildopts @@ -58,11 +63,6 @@ from modulemd.components.rpm import ModuleComponentRPM from modulemd.filter import ModuleFilter from modulemd.profile import ModuleProfile -import gi -from gi.repository import GLib -gi.require_version('Modulemd', '0.2') -from gi.repository import Modulemd - supported_mdversions = ( 1, ) def load_all(f): From 22b08d0f4bf4d1250d5014665e435912d300ae27 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 18/48] make pyflakes complain less --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 5ece126..f01e89f 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -643,33 +643,33 @@ class ModuleMetadata(object): elif type(item) == dict: l_variant.append(ModuleMetadata._variant_dict(item)) else: - raise TypeError ("Cannot convert unknown type") - return GLib.Variant('av', l_variant); + raise TypeError("Cannot convert unknown type") + return GLib.Variant('av', l_variant) def _variant_dict_values(d): if not isinstance(d, dict): - raise TypeError ("Only dictionaries are supported for mappings") + raise TypeError("Only dictionaries are supported for mappings") d_variant = dict() for k, v in d.items(): if type(v) == str: - d_variant[k] = ModuleMetadata._variant_str(v); + d_variant[k] = ModuleMetadata._variant_str(v) pass elif type(v) == list: - d_variant[k] = ModuleMetadata._variant_list(v); + d_variant[k] = ModuleMetadata._variant_list(v) elif type(v) == dict: - d_variant[k] = ModuleMetadata._variant_dict(v); + d_variant[k] = ModuleMetadata._variant_dict(v) else: - raise TypeError ("Cannot convert unknown type") + raise TypeError("Cannot convert unknown type") return d_variant def _variant_dict(d): if not isinstance(d, dict): - raise TypeError ("Only dictionaries are supported for mappings") + raise TypeError("Only dictionaries are supported for mappings") d_variant = ModuleMetadata._variant_dict_values(d) - return GLib.Variant('a{sv}', d_variant); + return GLib.Variant('a{sv}', d_variant) @xmd.setter @@ -686,7 +686,7 @@ class ModuleMetadata(object): """A dictionary property representing the module profiles.""" # TODO: return the profiles as a dict of ModuleProfile types d = dict() - for k,v in self.module.get_profiles().items(): + for k, v in self.module.get_profiles().items(): d[k] = ModuleProfile(profile=v, parent=self, name=k) return d @@ -701,7 +701,7 @@ class ModuleMetadata(object): profiles = self.module.get_profiles() - for k,v in d.items(): + for k, v in d.items(): profile = Modulemd.Profile() if v.description: profile.set_description(v.description) From 407c100b51db0442293fa79d0dd74a5fc49853cb Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 19/48] don't overwrite self._buildopts on each access --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index f01e89f..c40c21e 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -752,7 +752,8 @@ class ModuleMetadata(object): """A ModuleBuildopts instance representing the additional module components build options. """ - self._buildopts = ModuleBuildopts(parent=self) + if not getattr(self, '_buildopts', None): + self.buildopts = ModuleBuildopts(parent=self) return self._buildopts @buildopts.setter From 04e2d2f5efde8b161cd69b814780a055d1afe77b Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 20/48] convert ModuleComponents etc. to libmodulemd --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index c40c21e..4c8b448 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -767,13 +767,19 @@ class ModuleMetadata(object): """A ModuleComponents instance property representing the components defining the module. """ + if not hasattr(self, '_components'): + self._components = ModuleComponents(parent=self) + self._components.rpms = self.module.get_rpm_components() + self._components.modules = self.module.get_module_components() return self._components @components.setter def components(self, o): if not isinstance(o, ModuleComponents): raise TypeError("components: data type not supported") - self._components = o + + self.module.set_rpm_components(o.rpms) + self.module.set_module_components(o.modules) @property def artifacts(self): diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index 60ee9a6..b7ef95a 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -23,18 +23,22 @@ # # Written by Petr Šabata +from gi.repository import Modulemd + from modulemd.components.module import ModuleComponentModule from modulemd.components.rpm import ModuleComponentRPM + supported_content = ( "rpms", "modules", ) class ModuleComponents(object): """Class representing components of a module.""" - def __init__(self): + def __init__(self, parent=None): """Creates a new ModuleComponents instance.""" - self.modules = dict() - self.rpms = dict() + self.parent = parent + self._rpms = {} + self._modules = {} def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 21/48] only use libmodulemd functions in __init__() --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 4c8b448..83dadcb 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -159,10 +159,6 @@ class ModuleMetadata(object): self.module.set_module_components({}) self.module.set_rpm_artifacts(Modulemd.SimpleSet()) - self.buildopts = ModuleBuildopts() - self.components = ModuleComponents() - - def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 22/48] components: cope with native objects in setters --- diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index b7ef95a..005f3c8 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -108,8 +108,13 @@ class ModuleComponents(object): if not isinstance(d, dict): raise TypeError("components.rpms: data type not supported") for k, v in d.items(): - if not isinstance(k, str) or not isinstance(v, ModuleComponentRPM): - raise TypeError("components.rpms: data type not supported") + if not (isinstance(k, str) + and isinstance(v, (ModuleComponentRPM, + Modulemd.ComponentRpm))): + raise TypeError("components.rpms: data type not supported" + " ({!r}: {!r})".format(k, v)) + if isinstance(v, Modulemd.ComponentRpm): + d[k] = ModuleComponentRPM._new_from_native(k, v) self._rpms = d self._save_to_libmodulemd() @@ -155,8 +160,13 @@ class ModuleComponents(object): if not isinstance(d, dict): raise TypeError("components.modules: data type not supported") for k, v in d.items(): - if not isinstance(k, str) or not isinstance(v, ModuleComponentModule): - raise TypeError("components.modules: data type not supported") + if not (isinstance(k, str) + and isinstance(v, (ModuleComponentModule, + Modulemd.ComponentModule))): + raise TypeError("components.modules: data type not supported" + " ({!r}: {!r})".format(k, v)) + if isinstance(v, Modulemd.ComponentModule): + d[k] = ModuleComponentModule._new_from_native(k, v) self._modules = d self._save_to_libmodulemd() diff --git a/modulemd/components/module.py b/modulemd/components/module.py index 9a0d2c2..509d2dd 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -35,6 +35,13 @@ class ModuleComponentModule(ModuleComponentBase): self.repository = repository self.ref = ref + @classmethod + def _new_from_native(cls, name, native_comp): + return cls(name, native_comp.get_rationale(), + buildorder=native_comp.get_buildorder() or 0, + repository=native_comp.get_repository() or "", + ref=native_comp.get_ref() or "") + def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 23/48] implement ModuleComponent(RPM|Module).__eq__() This allows comparing objects with different identity for equality. This is needed because these objects usually have quite a short lifetime because they're frequently recreated from libmodulemd data. --- diff --git a/modulemd/components/base.py b/modulemd/components/base.py index 0fd97e0..07ec0cc 100644 --- a/modulemd/components/base.py +++ b/modulemd/components/base.py @@ -52,6 +52,14 @@ class ModuleComponentBase(object): __nonzero__ = __bool__ + def __eq__(self, other): + if type(self) != type(other): + return False + + return (self.name == other.name + and self.rationale == other.rationale + and self.buildorder == other.buildorder) + @property def name(self): """A string property representing the component name.""" diff --git a/modulemd/components/module.py b/modulemd/components/module.py index 509d2dd..4aaaf58 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -62,6 +62,13 @@ class ModuleComponentModule(ModuleComponentBase): __nonzero__ = __bool__ + def __eq__(self, other): + if not super(ModuleComponentModule, self).__eq__(other): + return False + + return (self.repository == other.repository + and self.ref == other.ref) + @property def repository(self): """A string property representing the VCS repository with the modulemd diff --git a/modulemd/components/rpm.py b/modulemd/components/rpm.py index b703af2..3e8c0e8 100644 --- a/modulemd/components/rpm.py +++ b/modulemd/components/rpm.py @@ -75,6 +75,16 @@ class ModuleComponentRPM(ModuleComponentBase): __nonzero__ = __bool__ + def __eq__(self, other): + if not super(ModuleComponentRPM, self).__eq__(other): + return False + + return (self.repository == other.repository + and self.ref == other.ref + and self.cache == other.cache + and self.arches == other.arches + and self.multilib == other.multilib) + @property def repository(self): """A string property representing the VCS repository with the RPM SPEC From 30e2d91b45352c6ea49a73dc48928ba450a35739 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 24/48] reformat license and copyright headers --- diff --git a/LICENSE b/LICENSE index 0e7dda2..73c5ddb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,21 @@ The MIT License (MIT) -Copyright (c) 2016 Red Hat, Inc. +Copyright © 2016 Red Hat, Inc. -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 83dadcb..682ba59 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/api.py b/modulemd/api.py index a48d848..e4862ab 100644 --- a/modulemd/api.py +++ b/modulemd/api.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/artifacts.py b/modulemd/artifacts.py index efdc228..88c20da 100644 --- a/modulemd/artifacts.py +++ b/modulemd/artifacts.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016, 2017 Red Hat, Inc. +# Copyright © 2016, 2017 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/buildopts/__init__.py b/modulemd/buildopts/__init__.py index 9f4cc11..ceac015 100644 --- a/modulemd/buildopts/__init__.py +++ b/modulemd/buildopts/__init__.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016, 2017 Red Hat, Inc. +# Copyright © 2016, 2017 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/buildopts/base.py b/modulemd/buildopts/base.py index e3000a8..b1d3fc0 100644 --- a/modulemd/buildopts/base.py +++ b/modulemd/buildopts/base.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016, 2017 Red Hat, Inc. +# Copyright © 2016, 2017 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/buildopts/rpms.py b/modulemd/buildopts/rpms.py index 6dc0640..4681ab3 100644 --- a/modulemd/buildopts/rpms.py +++ b/modulemd/buildopts/rpms.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016, 2017 Red Hat, Inc. +# Copyright © 2016, 2017 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index 005f3c8..af1014c 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/components/base.py b/modulemd/components/base.py index 07ec0cc..c00dd09 100644 --- a/modulemd/components/base.py +++ b/modulemd/components/base.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/components/module.py b/modulemd/components/module.py index 4aaaf58..db81e95 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/components/rpm.py b/modulemd/components/rpm.py index 3e8c0e8..63882e3 100644 --- a/modulemd/components/rpm.py +++ b/modulemd/components/rpm.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/filter.py b/modulemd/filter.py index b2cecd1..d4b91fc 100644 --- a/modulemd/filter.py +++ b/modulemd/filter.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/profile.py b/modulemd/profile.py index 9e2d2ad..4ddc5f9 100644 --- a/modulemd/profile.py +++ b/modulemd/profile.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -10,8 +9,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/tests/test_basic.py b/modulemd/tests/test_basic.py index 71bc3d7..8875357 100644 --- a/modulemd/tests/test_basic.py +++ b/modulemd/tests/test_basic.py @@ -1,8 +1,7 @@ #/usr/bin/python3 # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -11,8 +10,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/tests/test_convenience.py b/modulemd/tests/test_convenience.py index 4c40617..dbdaf55 100644 --- a/modulemd/tests/test_convenience.py +++ b/modulemd/tests/test_convenience.py @@ -1,8 +1,7 @@ #/usr/bin/python3 # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -11,8 +10,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index 0eff5db..8bddd9f 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -1,8 +1,7 @@ #/usr/bin/python3 # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -11,8 +10,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/modulemd/tests/test_validation.py b/modulemd/tests/test_validation.py index 563dd2a..4b54a61 100644 --- a/modulemd/tests/test_validation.py +++ b/modulemd/tests/test_validation.py @@ -1,8 +1,7 @@ #/usr/bin/python3 # -*- coding: utf-8 -*- - -# Copyright (c) 2016 Red Hat, Inc. +# Copyright © 2016 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -11,8 +10,8 @@ # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, From 940d0fd53145000bf91a5fafd2aa1455aa717fa2 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 25/48] add TreeObj and RPMsMixin classes These serve to share code between objects that represent non-leaf nodes of a modulemd tree and those that contain a set of RPM names, especially with regards to dispatching things to the native side, i.e. libmodulemd. --- diff --git a/modulemd/legacy_wrappers.py b/modulemd/legacy_wrappers.py new file mode 100644 index 0000000..a12d057 --- /dev/null +++ b/modulemd/legacy_wrappers.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- + + +# Copyright © 2016 - 2018 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +import inspect + +from gi.repository import Modulemd + + +class _TreeObjBase(object): + + # The defining properties of the particular tree object. Set this in child + # classes or a mixin. + tree_props = () + + # The name of the particular tree object. Set this in concrete child + # classes. + tree_identity = None + + +class TreeObj(_TreeObjBase): + """Class representing a part of the tree for the legacy Python API.""" + + def __init__(self, parent=None, **kwargs): + # self.parent is r/o + self._parent = parent + super(TreeObj, self).__init__(**kwargs) + + def __bool__(self): + return True + + def __nonzero__(self): + return self.__bool__() + + @property + def parent(self): + return self._parent + + def replace(self, o): + if not isinstance(o, type(self)): + raise TypeError( + "{}: data type not supported".format(type(o).__name__)) + + props = set() + for base in inspect.getmro(type(self)): + props.update(getattr(base, 'tree_props', ())) + + for prop in props: + setattr(self, prop, getattr(o, prop)) + + +class RPMsMixin(_TreeObjBase): + """Mixin supporting an 'rpms' property as a set. + + Needs get/set_rpm_set() implemented in child classes.""" + + tree_props = ('rpms',) + + def __init__(self, rpms=None, **kwargs): + if rpms is None: + rpms = set() + super(RPMsMixin, self).__init__(**kwargs) + self.rpms = rpms + + def __bool__(self): + return (super(RPMsMixin, self).__bool__() + or True if self.rpms else False) + + def get_rpm_set(self): + """Dispatch to native method for getting RPM sets. + + This will be something like self.parent.module.get_rpm_*() or an + appropriate method on the associated native object.""" + raise NotImplementedError() + + def set_rpm_set(self, ss): + """Dispatch to native method for setting RPM sets. + + This will be something like self.parent.module.set_rpm_*() or an + appropriate method on the associated native object.""" + raise NotImplementedError() + + @property + def rpms(self): + """A set of binary RPM packages defining this module's API.""" + if self.parent: + return set(self.get_rpm_set().get()) + else: + return self._rpms + + @rpms.setter + def rpms(self, ss): + if ss is not None: + if not isinstance(ss, set): + raise TypeError("{}.rpms: data type not supported:" + " {!r} (must be a set)".format( + self.tree_identity, ss)) + for v in ss: + if not isinstance(v, str): + raise TypeError("{}.rpms: data type not supported:" + " {!r} (elements must be strings)".format( + self.tree_identity, v)) + + if self.parent: + if ss is not None: + _ss = Modulemd.SimpleSet() + _ss.set(list(ss)) + else: + _ss = None + self.set_rpm_set(_ss) + else: + self._rpms = ss + + def add_rpm(self, s): + """Adds a binary RPM package to the API set. + + :param str s: Binary RPM package name + """ + if not isinstance(s, str): + raise TypeError("{}.add_rpm: data type not supported".format( + self.tree_identity)) + + if self.parent: + rpms = self.rpms + rpms.add(s) + self.rpms = rpms + else: + self._rpms.add(s) + + def del_rpm(self, s): + """Removes the supplied package name from the package set. + + :param str s: Binary RPM package name + """ + if not isinstance(s, str): + raise TypeError("api.del_rpm: data type not supported") + + if self.parent: + rpms = self.rpms + rpms.discard(s) + self.rpms = rpms + else: + self._rpms.discard(s) + + def clear_rpms(self): + """Clear the API binary RPM package set.""" + + if hasattr(self, 'parent'): + self.rpms = set() + else: + self._rpms.clear() From 498097857c92571ad12de033d4d0674f4d18e620 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 26/48] use relative imports within modulemd package --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 682ba59..5deadc5 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -52,15 +52,15 @@ from gi.repository import GLib gi.require_version('Modulemd', '0.2') from gi.repository import Modulemd -from modulemd.api import ModuleAPI -from modulemd.artifacts import ModuleArtifacts -from modulemd.buildopts import ModuleBuildopts -from modulemd.buildopts.rpms import ModuleBuildoptsRPMs -from modulemd.components import ModuleComponents -from modulemd.components.module import ModuleComponentModule -from modulemd.components.rpm import ModuleComponentRPM -from modulemd.filter import ModuleFilter -from modulemd.profile import ModuleProfile +from .api import ModuleAPI +from .artifacts import ModuleArtifacts +from .buildopts import ModuleBuildopts +from .buildopts.rpms import ModuleBuildoptsRPMs +from .components import ModuleComponents +from .components.module import ModuleComponentModule +from .components.rpm import ModuleComponentRPM +from .filter import ModuleFilter +from .profile import ModuleProfile supported_mdversions = ( 1, ) diff --git a/modulemd/buildopts/__init__.py b/modulemd/buildopts/__init__.py index ceac015..21691f6 100644 --- a/modulemd/buildopts/__init__.py +++ b/modulemd/buildopts/__init__.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -from modulemd.buildopts.rpms import ModuleBuildoptsRPMs +from .rpms import ModuleBuildoptsRPMs supported_content = ( "rpms", ) diff --git a/modulemd/buildopts/rpms.py b/modulemd/buildopts/rpms.py index 4681ab3..a552e0c 100644 --- a/modulemd/buildopts/rpms.py +++ b/modulemd/buildopts/rpms.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -from modulemd.buildopts.base import ModuleBuildoptsBase +from .base import ModuleBuildoptsBase class ModuleBuildoptsRPMs(ModuleBuildoptsBase): """A buildopts class for handling RPM content build options.""" diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index af1014c..44bf4ab 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -24,8 +24,8 @@ from gi.repository import Modulemd -from modulemd.components.module import ModuleComponentModule -from modulemd.components.rpm import ModuleComponentRPM +from .module import ModuleComponentModule +from .rpm import ModuleComponentRPM supported_content = ( "rpms", "modules", ) diff --git a/modulemd/components/module.py b/modulemd/components/module.py index db81e95..efa9cfb 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -from modulemd.components.base import ModuleComponentBase +from .base import ModuleComponentBase class ModuleComponentModule(ModuleComponentBase): """A component class for handling module-type content.""" diff --git a/modulemd/components/rpm.py b/modulemd/components/rpm.py index 63882e3..96d305f 100644 --- a/modulemd/components/rpm.py +++ b/modulemd/components/rpm.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -from modulemd.components.base import ModuleComponentBase +from .base import ModuleComponentBase class ModuleComponentRPM(ModuleComponentBase): """A component class for handling RPM content.""" From 481460bae470d13527fd3e33fd57718b4c2b2ac4 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 27/48] don't fake `long` type in Python 3 Instead, do it like six without actually pulling it in. --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 5deadc5..89cacf3 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -44,9 +44,6 @@ import datetime import dateutil.parser import yaml -if sys.version_info > (3,): - long = int - import gi from gi.repository import GLib gi.require_version('Modulemd', '0.2') @@ -62,6 +59,12 @@ from .components.rpm import ModuleComponentRPM from .filter import ModuleFilter from .profile import ModuleProfile + +if sys.version_info > (3,): + integer_types = (int,) +else: + integer_types = (int, long) # noqa + supported_mdversions = ( 1, ) def load_all(f): @@ -263,7 +266,7 @@ class ModuleMetadata(object): @mdversion.setter def mdversion(self, i): - if not isinstance(i, (int, long)): + if not isinstance(i, integer_types): raise TypeError("mdversion: data type not supported") if i not in supported_mdversions: raise ValueError("mdversion: document version not supported") @@ -298,7 +301,7 @@ class ModuleMetadata(object): @version.setter def version(self, i): - if not isinstance(i, (int, long)): + if not isinstance(i, integer_types): raise TypeError("version: data type not supported") if i < 0: raise ValueError("version: version cannot be negative") diff --git a/modulemd/components/base.py b/modulemd/components/base.py index c00dd09..adedb65 100644 --- a/modulemd/components/base.py +++ b/modulemd/components/base.py @@ -25,7 +25,10 @@ import sys if sys.version_info > (3,): - long = int + integer_types = (int,) +else: + integer_types = (int, long) + class ModuleComponentBase(object): """A base class for definining module component types.""" @@ -92,6 +95,6 @@ class ModuleComponentBase(object): @buildorder.setter def buildorder(self, i): - if not isinstance(i, (int, long)): + if not isinstance(i, integer_types): raise TypeError("componentbase.buildorder: data type not supported") self._buildorder = i From e294cff2872e874f06a3160a3879e6e4c11dedf0 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 28/48] pep8: remove unused imports, reformat some things --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 89cacf3..6136167 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -41,7 +41,6 @@ Example usage: import sys import datetime -import dateutil.parser import yaml import gi @@ -52,10 +51,7 @@ from gi.repository import Modulemd from .api import ModuleAPI from .artifacts import ModuleArtifacts from .buildopts import ModuleBuildopts -from .buildopts.rpms import ModuleBuildoptsRPMs from .components import ModuleComponents -from .components.module import ModuleComponentModule -from .components.rpm import ModuleComponentRPM from .filter import ModuleFilter from .profile import ModuleProfile @@ -65,7 +61,8 @@ if sys.version_info > (3,): else: integer_types = (int, long) # noqa -supported_mdversions = ( 1, ) +supported_mdversions = (1,) + def load_all(f): """Loads a metadata file containing multiple modulemd documents @@ -83,6 +80,7 @@ def load_all(f): return l + def loads_all(s): """Loads multiple modulemd documents from a YAML multidocument string. @@ -99,6 +97,7 @@ def loads_all(s): return l + def dump_all(f, l): """Dumps a list of ModuleMetadata instances into a file. @@ -111,6 +110,7 @@ def dump_all(f, l): return Modulemd.Module.dump_all(mmds, f) + def dumps_all(l): """Dumps a list of ModuleMetadata instance into a YAML multidocument string. @@ -123,6 +123,7 @@ def dumps_all(l): return Modulemd.Module.dumps_all(mmds) + class ModuleMetadata(object): """Class representing the whole module.""" @@ -162,52 +163,20 @@ class ModuleMetadata(object): self.module.set_rpm_artifacts(Modulemd.SimpleSet()) def __repr__(self): - return ("").format( - repr(self.mdversion), - repr(self.name), - repr(self.stream), - repr(self.version), - repr(self.context), - repr(self.arch), - repr(self.summary), - repr(self.description), - repr(self.eol), - repr(sorted(self.module_licenses)), - repr(sorted(self.content_licenses)), - repr(sorted(self.buildrequires)), - repr(sorted(self.requires)), - repr(self.community), - repr(self.documentation), - repr(self.tracker), - repr(sorted(self.xmd)), - repr(sorted(self.profiles)), - repr(self.api), - repr(self.filter), - repr(self.components), - repr(self.artifacts) - ) + return ("").format( + self.mdversion, self.name, self.stream, self.version, + self.context, self.arch, self.summary, self.description, + self.eol, sorted(self.module_licenses), + sorted(self.content_licenses), sorted(self.buildrequires), + sorted(self.requires), self.community, self.documentation, + self.tracker, sorted(self.xmd), sorted(self.profiles), + self.api, self.filter, self.components, self.artifacts) def load(self, f): """Loads a metadata file into the instance. @@ -669,7 +638,6 @@ class ModuleMetadata(object): return GLib.Variant('a{sv}', d_variant) - @xmd.setter def xmd(self, d): if not isinstance(d, dict): diff --git a/modulemd/api.py b/modulemd/api.py index e4862ab..3821daf 100644 --- a/modulemd/api.py +++ b/modulemd/api.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -supported_content = ( "rpms", ) +supported_content = ("rpms",) from gi.repository import Modulemd @@ -35,10 +35,7 @@ class ModuleAPI(object): self.parent = parent def __repr__(self): - return ("").format( - repr(sorted(self.rpms)) - ) + return "".format(sorted(self.rpms)) def __bool__(self): return True if self.rpms else False diff --git a/modulemd/artifacts.py b/modulemd/artifacts.py index 88c20da..b42d75d 100644 --- a/modulemd/artifacts.py +++ b/modulemd/artifacts.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -supported_content = ( "rpms", ) +supported_content = ("rpms",) from gi.repository import Modulemd @@ -35,10 +35,7 @@ class ModuleArtifacts(object): self.parent = parent def __repr__(self): - return ("").format( - repr(sorted(self.rpms)) - ) + return "".format(sorted(self.rpms)) def __bool__(self): return True if self.rpms else False diff --git a/modulemd/buildopts/__init__.py b/modulemd/buildopts/__init__.py index 21691f6..b92c9fd 100644 --- a/modulemd/buildopts/__init__.py +++ b/modulemd/buildopts/__init__.py @@ -24,7 +24,7 @@ from .rpms import ModuleBuildoptsRPMs -supported_content = ( "rpms", ) +supported_content = ("rpms",) class ModuleBuildopts(object): """Class representing component build options.""" @@ -36,10 +36,7 @@ class ModuleBuildopts(object): def __repr__(self): - return ("").format( - repr(self.rpms) - ) + return "".format(self.rpms) def __bool__(self): return True if self.rpms else False diff --git a/modulemd/buildopts/base.py b/modulemd/buildopts/base.py index b1d3fc0..870e6c9 100644 --- a/modulemd/buildopts/base.py +++ b/modulemd/buildopts/base.py @@ -22,7 +22,6 @@ # # Written by Petr Šabata -import sys class ModuleBuildoptsBase(object): """A base class for definining component build options.""" diff --git a/modulemd/buildopts/rpms.py b/modulemd/buildopts/rpms.py index a552e0c..d601d1e 100644 --- a/modulemd/buildopts/rpms.py +++ b/modulemd/buildopts/rpms.py @@ -24,6 +24,7 @@ from .base import ModuleBuildoptsBase + class ModuleBuildoptsRPMs(ModuleBuildoptsBase): """A buildopts class for handling RPM content build options.""" @@ -49,10 +50,7 @@ class ModuleBuildoptsRPMs(ModuleBuildoptsBase): def __repr__(self): - return ("").format( - repr(self.macros) - ) + return "".format(self.macros) def __bool__(self): return True if self.macros else False diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index 44bf4ab..027c73e 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -28,7 +28,8 @@ from .module import ModuleComponentModule from .rpm import ModuleComponentRPM -supported_content = ( "rpms", "modules", ) +supported_content = ("rpms", "modules",) + class ModuleComponents(object): """Class representing components of a module.""" @@ -40,12 +41,8 @@ class ModuleComponents(object): self._modules = {} def __repr__(self): - return ("").format( - repr(sorted(self.modules)), - repr(sorted(self.rpms)) - ) + return "".format( + sorted(self.modules), sorted(self.rpms)) def __bool__(self): return True if self.all else False diff --git a/modulemd/components/base.py b/modulemd/components/base.py index adedb65..96da94d 100644 --- a/modulemd/components/base.py +++ b/modulemd/components/base.py @@ -40,14 +40,9 @@ class ModuleComponentBase(object): self.buildorder = buildorder def __repr__(self): - return ("").format( - repr(self.name), - repr(self.rationale), - repr(self.buildorder) - ) + return ("").format(self.name, self.rationale, + self.buildorder) def __bool__(self): return True if (self.name or self.rationale or self.buildorder) else False diff --git a/modulemd/components/module.py b/modulemd/components/module.py index efa9cfb..31dbfc7 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -24,11 +24,12 @@ from .base import ModuleComponentBase + class ModuleComponentModule(ModuleComponentBase): """A component class for handling module-type content.""" def __init__(self, name, rationale, buildorder=0, - repository="", ref=""): + repository="", ref=""): """Creates a new ModuleComponentModule instance.""" super(ModuleComponentModule, self).__init__(name, rationale, buildorder) self.repository = repository @@ -42,18 +43,10 @@ class ModuleComponentModule(ModuleComponentBase): ref=native_comp.get_ref() or "") def __repr__(self): - return ("").format( - repr(self.name), - repr(self.rationale), - repr(self.buildorder), - repr(self.repository), - repr(self.ref) - ) + return ("").format( + self.name, self.rationale, self.buildorder, + self.repository, self.ref) def __bool__(self): return True if (self.name or self.rationale or self.buildorder or diff --git a/modulemd/components/rpm.py b/modulemd/components/rpm.py index 96d305f..9a6e47b 100644 --- a/modulemd/components/rpm.py +++ b/modulemd/components/rpm.py @@ -24,11 +24,13 @@ from .base import ModuleComponentBase + class ModuleComponentRPM(ModuleComponentBase): """A component class for handling RPM content.""" def __init__(self, name, rationale, buildorder=0, - repository="", ref="", cache="", arches=set(), multilib=set()): + repository="", ref="", cache="", arches=set(), + multilib=set()): """Creates a new ModuleComponentRPM instance.""" super(ModuleComponentRPM, self).__init__(name, rationale, buildorder) self.repository = repository @@ -48,24 +50,12 @@ class ModuleComponentRPM(ModuleComponentBase): multilib=set(native_comp.get_multilib().get())) def __repr__(self): - return ("").format( - repr(self.name), - repr(self.rationale), - repr(self.buildorder), - repr(self.repository), - repr(self.ref), - repr(self.cache), - repr(sorted(self.arches)), - repr(sorted(self.multilib)) - ) + return ("").format( + self.name, self.rationale, self.buildorder, + self.repository, self.ref, self.cache, sorted(self.arches), + sorted(self.multilib)) def __bool__(self): return True if (self.name or self.rationale or self.buildorder or diff --git a/modulemd/filter.py b/modulemd/filter.py index d4b91fc..630d9c3 100644 --- a/modulemd/filter.py +++ b/modulemd/filter.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -supported_content = ( "rpms", ) +supported_content = ("rpms",) from gi.repository import Modulemd @@ -35,10 +35,7 @@ class ModuleFilter(object): self.parent = parent def __repr__(self): - return ("").format( - repr(sorted(self.rpms)) - ) + return "".format(sorted(self.rpms)) def __bool__(self): return True if self.rpms else False diff --git a/modulemd/profile.py b/modulemd/profile.py index 4ddc5f9..abd515a 100644 --- a/modulemd/profile.py +++ b/modulemd/profile.py @@ -22,7 +22,7 @@ # # Written by Petr Šabata -supported_content = ( "rpms", ) +supported_content = ("rpms",) from gi.repository import Modulemd @@ -45,12 +45,9 @@ class ModuleProfile(object): self.name = name def __repr__(self): - return ("").format( - repr(self.description), - repr(sorted(self.rpms)) - ) + return ("").format(self.name, self.description, + sorted(self.rpms)) def __bool__(self): return True if (self.description or self.rpms) else False From ae82ee361d00711c4c5f27bcd492ba10009831f6 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 29/48] reset tree new-style --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 6136167..d04e5bc 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -134,33 +134,7 @@ class ModuleMetadata(object): # Under the hood, we will use the ModulemdModule type self.module = Modulemd.Module() - self.module.set_mdversion(max(supported_mdversions)) - self.module.set_name("") - self.module.set_stream("") - self.module.set_version(0) - self.module.set_context("") - self.module.set_arch("") - self.module.set_summary("") - self.module.set_description("") - # Don't pre-set EOL, since we don't have a valid value yet - # The Modulemd.Module() default initializes it to an appropriate invalid - # date. - self.module.set_servicelevels({}) - self.module.set_module_licenses(Modulemd.SimpleSet()) - self.module.set_content_licenses(Modulemd.SimpleSet()) - self.module.set_buildrequires({}) - self.module.set_requires({}) - self.module.set_community("") - self.module.set_documentation("") - self.module.set_tracker("") - self.module.set_xmd({}) - self.module.set_profiles({}) - self.module.set_rpm_api(Modulemd.SimpleSet()) - self.module.set_rpm_filter(Modulemd.SimpleSet()) - self.module.set_rpm_buildopts({}) - self.module.set_rpm_components({}) - self.module.set_module_components({}) - self.module.set_rpm_artifacts(Modulemd.SimpleSet()) + self.reset() def __repr__(self): return (" Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 30/48] allow unsetting eol date --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index d04e5bc..41f8425 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -161,10 +161,7 @@ class ModuleMetadata(object): self.arch = "" self.summary = "" self.description = "" - # Don't pre-set EOL, since we don't have a valid value yet - # The Modulemd.Module() default initializes it to an appropriate invalid - # date. - #self.eol = None + self.eol = None self.servicelevels = {} self.module_licenses = set() self.content_licenses = set() @@ -344,7 +341,10 @@ class ModuleMetadata(object): if not isinstance(o, datetime.date) and o is not None: raise TypeError("eol: data type not supported") - d = GLib.Date.new_dmy(o.day, o.month, o.year) + if o: + d = GLib.Date.new_dmy(o.day, o.month, o.year) + else: + d = None self.module.set_eol(d) @property From c83491ea0c9004165962819c4acf115bd0d24f39 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 31/48] fix servicelevels property setter --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 41f8425..b723baf 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -355,7 +355,9 @@ class ModuleMetadata(object): @servicelevels.setter def servicelevels(self, d): if not isinstance(d, dict): - self.module.set_servicelevels(d) + raise TypeError("servicelevels: data type not supported") + + self.module.set_servicelevels(d) @property def module_licenses(self): From 7dfca79f57c7473df8e1047f99fcc9dbaf708e5a Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 32/48] make _variant_*() methods real class/static methods --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index b723baf..48d1ee9 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -599,13 +599,15 @@ class ModuleMetadata(object): d[key] = value.unpack() return d + @staticmethod def _variant_str(s): if not isinstance(s, str): raise TypeError ("Only strings are supported for scalars") return GLib.Variant('s', s) - def _variant_list(l): + @classmethod + def _variant_list(cls, l): l_variant = list() for item in l: if type(item) == str: @@ -618,28 +620,30 @@ class ModuleMetadata(object): raise TypeError("Cannot convert unknown type") return GLib.Variant('av', l_variant) - def _variant_dict_values(d): + @classmethod + def _variant_dict_values(cls, d): if not isinstance(d, dict): raise TypeError("Only dictionaries are supported for mappings") d_variant = dict() for k, v in d.items(): if type(v) == str: - d_variant[k] = ModuleMetadata._variant_str(v) + d_variant[k] = cls._variant_str(v) pass elif type(v) == list: - d_variant[k] = ModuleMetadata._variant_list(v) + d_variant[k] = cls._variant_list(v) elif type(v) == dict: - d_variant[k] = ModuleMetadata._variant_dict(v) + d_variant[k] = cls._variant_dict(v) else: raise TypeError("Cannot convert unknown type") return d_variant - def _variant_dict(d): + @classmethod + def _variant_dict(cls, d): if not isinstance(d, dict): raise TypeError("Only dictionaries are supported for mappings") - d_variant = ModuleMetadata._variant_dict_values(d) + d_variant = cls._variant_dict_values(d) return GLib.Variant('a{sv}', d_variant) @@ -648,7 +652,7 @@ class ModuleMetadata(object): if not isinstance(d, dict): raise TypeError("xmd: data type not supported") - xmd = ModuleMetadata._variant_dict_values(d) + xmd = self._variant_dict_values(d) self.module.set_xmd(xmd) From 58d8132a4221506786b0aaddb44288598aa7b608 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 33/48] migrate ModuleAPI to TreeObj/RPMsMixin --- diff --git a/modulemd/api.py b/modulemd/api.py index 3821daf..29ce080 100644 --- a/modulemd/api.py +++ b/modulemd/api.py @@ -22,78 +22,22 @@ # # Written by Petr Šabata -supported_content = ("rpms",) +from .legacy_wrappers import TreeObj, RPMsMixin -from gi.repository import Modulemd -class ModuleAPI(object): +supported_content = ('rpms',) + + +class ModuleAPI(TreeObj, RPMsMixin): """Class representing a particular module API.""" - def __init__(self, parent=None): - """Creates a new ModuleAPI instance.""" - self.rpms = set() - self.parent = parent + tree_identity = 'api' def __repr__(self): return "".format(sorted(self.rpms)) - def __bool__(self): - return True if self.rpms else False - - __nonzero__ = __bool__ - - @property - def rpms(self): - """A set of binary RPM packages defining this module's API.""" - return self._rpms - - @rpms.setter - def rpms(self, ss): - if not isinstance(ss, set): - raise TypeError("api.rpms: data type not supported") - for v in ss: - if not isinstance(v, str): - raise TypeError("api.rpms: data type not supported") - self._rpms = ss - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_api(ss) - - def add_rpm(self, s): - """Adds a binary RPM package to the API set. - - :param str s: Binary RPM package name - """ - if not isinstance(s, str): - raise TypeError("api.add_rpm: data type not supported") - self._rpms.add(s) - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_api(ss) - - def del_rpm(self, s): - """Removes the supplied package name from the API package set. - - :param str s: Binary RPM package name - """ - if not isinstance(s, str): - raise TypeError("api.del_rpm: data type not supported") - self._rpms.discard(s) - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_api(ss) - - def clear_rpms(self): - """Clear the API binary RPM package set.""" - self._rpms.clear() + def get_rpm_set(self): + return self.parent.module.get_rpm_api() - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_api(ss) + def set_rpm_set(self, ss): + self.parent.module.set_rpm_api(ss) From 0dd8f1715dc20723e76d4b72d05f14b480c7ba13 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 34/48] migrate ModuleArtifacts to TreeObj/RPMsMixin --- diff --git a/modulemd/artifacts.py b/modulemd/artifacts.py index b42d75d..e575f2f 100644 --- a/modulemd/artifacts.py +++ b/modulemd/artifacts.py @@ -22,78 +22,28 @@ # # Written by Petr Šabata +from .legacy_wrappers import TreeObj, RPMsMixin + + supported_content = ("rpms",) -from gi.repository import Modulemd -class ModuleArtifacts(object): +class ModuleArtifacts(TreeObj, RPMsMixin): """Class representing a particular module artifacts.""" - def __init__(self, parent=None): + tree_identity = 'artifacts' + + def __init__(self, rpms=None, parent=None): """Creates a new ModuleArtifacts instance.""" - self.rpms = set() - self.parent = parent + super(ModuleArtifacts, self).__init__(parent=parent) + if rpms: + self.rpms = rpms def __repr__(self): return "".format(sorted(self.rpms)) - def __bool__(self): - return True if self.rpms else False - - __nonzero__ = __bool__ - - @property - def rpms(self): - """A set of NEVRAs listing this module's RPM artifacts.""" - return self._rpms - - @rpms.setter - def rpms(self, ss): - if not isinstance(ss, set): - raise TypeError("artifacts.rpms: data type not supported") - for v in ss: - if not isinstance(v, str): - raise TypeError("artifacts.rpms: data type not supported") - self._rpms = ss - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_artifacts(ss) - - def add_rpm(self, s): - """Adds an NEVRA to the artifact set. - - :param str s: RPM NEVRA - """ - if not isinstance(s, str): - raise TypeError("artifacts.add_rpm: data type not supported") - self._rpms.add(s) - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_artifacts(ss) - - def del_rpm(self, s): - """Removes the supplied NEVRA from the artifact set. - - :param str s: RPM NEVRA - """ - if not isinstance(s, str): - raise TypeError("artifacts.del_rpm: data type not supported") - self._rpms.discard(s) - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_artifacts(ss) - - def clear_rpms(self): - """Clear the RPM artifacts set.""" - self._rpms.clear() + def get_rpm_set(self): + return self.parent.module.get_rpm_artifacts() - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_artifacts(ss) + def set_rpm_set(self, ss): + self.parent.module.set_rpm_artifacts(ss) From d87fe7325f1095452d5129c312e4cb50fad22655 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 35/48] migrate ModuleFilter to TreeObj/RPMsMixin --- diff --git a/modulemd/filter.py b/modulemd/filter.py index 630d9c3..c1e47a7 100644 --- a/modulemd/filter.py +++ b/modulemd/filter.py @@ -22,78 +22,22 @@ # # Written by Petr Šabata +from .legacy_wrappers import TreeObj, RPMsMixin + + supported_content = ("rpms",) -from gi.repository import Modulemd -class ModuleFilter(object): +class ModuleFilter(TreeObj, RPMsMixin): """Class representing a particular module filter.""" - def __init__(self, parent=None): - """Creates a new ModuleFilter instance.""" - self.rpms = set() - self.parent = parent + tree_identity = 'filter' def __repr__(self): return "".format(sorted(self.rpms)) - def __bool__(self): - return True if self.rpms else False - - __nonzero__ = __bool__ - - @property - def rpms(self): - """A set of binary RPM packages defining this module's filter.""" - return self._rpms - - @rpms.setter - def rpms(self, ss): - if not isinstance(ss, set): - raise TypeError("filter.rpms: data type not supported") - for v in ss: - if not isinstance(v, str): - raise TypeError("filter.rpms: data type not supported") - self._rpms = ss - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_filter(ss) - - def add_rpm(self, s): - """Adds a binary RPM package to the filter set. - - :param str s: Binary RPM package name - """ - if not isinstance(s, str): - raise TypeError("filter.add_rpm: data type not supported") - self._rpms.add(s) - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_filter(ss) - - def del_rpm(self, s): - """Removes the supplied package name from the filter package set. - - :param str s: Binary RPM package name - """ - if not isinstance(s, str): - raise TypeError("filter.del_rpm: data type not supported") - self._rpms.discard(s) - - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_filter(ss) - - def clear_rpms(self): - """Clear the filter binary RPM package set.""" - self._rpms.clear() + def get_rpm_set(self): + return self.parent.module.get_rpm_filter() - if hasattr(self, 'parent'): - ss = Modulemd.SimpleSet() - ss.set(list(self.rpms)) - self.parent.module.set_rpm_filter(ss) + def set_rpm_set(self, ss): + self.parent.module.set_rpm_filter(ss) From c4570c271d54228f709a8ef65871a7481642d48f Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 36/48] migrate ModuleBuildopts* to TreeObj --- diff --git a/modulemd/buildopts/__init__.py b/modulemd/buildopts/__init__.py index b92c9fd..9e7e06f 100644 --- a/modulemd/buildopts/__init__.py +++ b/modulemd/buildopts/__init__.py @@ -22,18 +22,24 @@ # # Written by Petr Šabata +from ..legacy_wrappers import TreeObj + from .rpms import ModuleBuildoptsRPMs -supported_content = ("rpms",) -class ModuleBuildopts(object): +supported_content = ('rpms',) + + +class ModuleBuildopts(TreeObj): """Class representing component build options.""" - def __init__(self, parent=None): - """Creates a new ModuleBuildopts instance.""" - self.rpms = ModuleBuildoptsRPMs(parent=parent) - self.parent = parent + tree_props = ('rpms',) + tree_identity = 'buildopts' + def __init__(self, rpms=None, parent=None): + """Creates a new ModuleBuildopts instance.""" + super(ModuleBuildopts, self).__init__(parent=parent) + self._rpms = ModuleBuildoptsRPMs(parent=parent) def __repr__(self): return "".format(self.rpms) @@ -54,9 +60,8 @@ class ModuleBuildopts(object): def rpms(self, o): if not isinstance(o, ModuleBuildoptsRPMs): raise TypeError("buildopts.rpms: data type not supported") - self._rpms = o - if hasattr(self, 'parent') and self.parent is not None: - opts = dict() - opts['macros'] = o.macros - self.parent.set_buildopts(opts) + if self.parent: + self._rpms.replace(o) + else: + self._rpms = o diff --git a/modulemd/buildopts/base.py b/modulemd/buildopts/base.py index 870e6c9..f82ac7e 100644 --- a/modulemd/buildopts/base.py +++ b/modulemd/buildopts/base.py @@ -22,12 +22,13 @@ # # Written by Petr Šabata +from ..legacy_wrappers import TreeObj -class ModuleBuildoptsBase(object): - """A base class for definining component build options.""" - def __init__(self): - """Creates a new ModuleBuildoptsBase instance.""" +class ModuleBuildoptsBase(TreeObj): + """A base class for defining component build options. + + Nobody knows why it exists.""" def __repr__(self): return ("") diff --git a/modulemd/buildopts/rpms.py b/modulemd/buildopts/rpms.py index d601d1e..2ca97f7 100644 --- a/modulemd/buildopts/rpms.py +++ b/modulemd/buildopts/rpms.py @@ -30,24 +30,9 @@ class ModuleBuildoptsRPMs(ModuleBuildoptsBase): def __init__(self, macros="", parent=None): """Creates a new ModuleBuildoptsRPMs instance.""" - super(ModuleBuildoptsRPMs, self).__init__() - self.macros = macros - self.parent = parent - - # If macros were specified at initialization, update the - # module with it - if self.macros: - if hasattr(self, 'parent'): - opts = dict() - opts['macros'] = self.macros - self.parent.module.set_rpm_buildopts(opts) - return - - if parent: - m = self.parent.module.get_rpm_buildopts() - if 'macros' in m: - self.macros = m['macros'] + super(ModuleBuildoptsRPMs, self).__init__(parent=parent) + self.macros = macros def __repr__(self): return "".format(self.macros) @@ -62,15 +47,23 @@ class ModuleBuildoptsRPMs(ModuleBuildoptsBase): """A string property representing the additional RPM macros that should be used for this module build. """ - return self._macros + if self.parent: + rpm_buildopts = self.parent.module.get_rpm_buildopts() + if rpm_buildopts: + return rpm_buildopts['macros'] + else: + return "" + else: + return self._macros @macros.setter def macros(self, s): if not isinstance(s, str): raise TypeError("buildoptsrpm.macros: data type not supported") - self._macros = s - if hasattr(self, 'parent') and self.parent is not None: + if self.parent: opts = dict() - opts['macros'] = self.macros + opts['macros'] = s self.parent.module.set_rpm_buildopts(opts) + else: + self._macros = s From af315ad0b41fe297aa98e2918e9e7aae9048cf2e Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 18 2018 16:36:31 +0000 Subject: [PATCH 37/48] migrate ModuleComponent* to TreeObj --- diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index 027c73e..52c812a 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -24,6 +24,8 @@ from gi.repository import Modulemd +from ..legacy_wrappers import TreeObj + from .module import ModuleComponentModule from .rpm import ModuleComponentRPM @@ -31,14 +33,17 @@ from .rpm import ModuleComponentRPM supported_content = ("rpms", "modules",) -class ModuleComponents(object): +class ModuleComponents(TreeObj): """Class representing components of a module.""" + tree_props = ('rpms', 'modules') + tree_identity = 'components' + def __init__(self, parent=None): """Creates a new ModuleComponents instance.""" - self.parent = parent - self._rpms = {} - self._modules = {} + super(ModuleComponents, self).__init__(parent=parent) + self.rpms = {} + self.modules = {} def __repr__(self): return "".format( @@ -97,7 +102,11 @@ class ModuleComponents(object): """A dictionary of RPM components in this module. The keys are SRPM names, the values ModuleComponentRPM instances. """ - return self._rpms + if self.parent: + return {k: ModuleComponentRPM._new_from_native(v, parent=self.parent) + for k, v in self.parent.module.get_rpm_components().items()} + else: + return self._rpms @rpms.setter def rpms(self, d): @@ -109,47 +118,62 @@ class ModuleComponents(object): Modulemd.ComponentRpm))): raise TypeError("components.rpms: data type not supported" " ({!r}: {!r})".format(k, v)) - if isinstance(v, Modulemd.ComponentRpm): - d[k] = ModuleComponentRPM._new_from_native(k, v) - self._rpms = d + if not self.parent and isinstance(v, Modulemd.ComponentRpm): + d[k] = ModuleComponentRPM._new_from_native(v) + elif self.parent and isinstance(v, ModuleComponentRPM): + d[k] = v._to_native() - self._save_to_libmodulemd() + if self.parent: + self.parent.module.set_rpm_components(d) + else: + self._rpms = d def add_rpm(self, name, rationale, buildorder=0, repository="", ref="", cache="", arches=set(), multilib=set()): """Adds an RPM to the set of module components.""" - component = ModuleComponentRPM(name, rationale) - component.buildorder = buildorder - component.repository = repository - component.ref = ref - component.cache = cache - component.arches = arches - component.multilib = multilib - self._rpms[name] = component - - self._save_to_libmodulemd() + component = ModuleComponentRPM(name=name, rationale=rationale, + buildorder=buildorder, + repository=repository, ref=ref, + cache=cache, arches=arches, + multilib=multilib, + parent=self.parent) + + if self.parent: + rpms = self.rpms + rpms[name] = component + self.rpms = rpms + else: + self._rpms[name] = component def del_rpm(self, s): """Removes the supplied RPM from the set of module components.""" if not isinstance(s, str): raise TypeError("components.del_rpm: data type not supported") - if s in self._rpms: - del self._rpms[s] - - self._save_to_libmodulemd() + if self.parent: + rpms = self.rpms + rpms.pop(s, None) + self.rpms = rpms + else: + try: + del self._rpms[s] + except KeyError: + pass def clear_rpms(self): """Clear the RPM component dictionary.""" - self._rpms.clear() - - self._save_to_libmodulemd() + self.rpms = {} @property def modules(self): """A dictionary of module-type components in this module. The keys are module names, the values ModuleComponentModule instances. """ - return self._modules + if self.parent: + return {k: ModuleComponentModule._new_from_native(v, parent=self.parent) + for k, v in + self.parent.module.get_module_components().items()} + else: + return self._modules @modules.setter def modules(self, d): @@ -161,33 +185,43 @@ class ModuleComponents(object): Modulemd.ComponentModule))): raise TypeError("components.modules: data type not supported" " ({!r}: {!r})".format(k, v)) - if isinstance(v, Modulemd.ComponentModule): + if not self.parent and isinstance(v, Modulemd.ComponentModule): d[k] = ModuleComponentModule._new_from_native(k, v) - self._modules = d + elif self.parent and isinstance(v, ModuleComponentModule): + d[k] = v._to_native() - self._save_to_libmodulemd() + if self.parent: + self.parent.module.set_module_components(d) + else: + self._modules = d def add_module(self, name, rationale, buildorder=0, repository="", ref=""): - component = ModuleComponentModule(name, rationale) - component.buildorder = buildorder - component.repository = repository - component.ref = ref - self._modules[name] = component + component = ModuleComponentModule(name=name, rationale=rationale, + buildorder=buildorder, + repository=repository, ref=ref) - self._save_to_libmodulemd() + if self.parent: + modules = self.modules + modules[name] = component + self.modules = modules + else: + self._modules[name] = component def del_module(self, s): """Removes the supplied module from the set of module components.""" if not isinstance(s, str): raise TypeError("components.del_module: data type not supported") - if s in self._modules: - del self._modules[s] - - self._save_to_libmodulemd() + if self.parent: + modules = self.modules + modules.pop(s, None) + self.modules = modules + else: + try: + del self._modules[s] + except KeyError: + pass def clear_modules(self): """Clear the module-type component dictionary.""" - self._modules.clear() - - self._save_to_libmodulemd() + self.modules = {} diff --git a/modulemd/components/base.py b/modulemd/components/base.py index 96da94d..bf02033 100644 --- a/modulemd/components/base.py +++ b/modulemd/components/base.py @@ -24,17 +24,28 @@ import sys +from ..legacy_wrappers import TreeObj + + if sys.version_info > (3,): integer_types = (int,) else: integer_types = (int, long) -class ModuleComponentBase(object): +class ModuleComponentBase(TreeObj): """A base class for definining module component types.""" - def __init__(self, name, rationale, buildorder=0): + tree_props = ('name', 'rationale', 'buildorder') + + # set this to a child class of Modulemd.Component + component_class = None + + def __init__(self, name, rationale, buildorder=0, parent=None): """Creates a new ModuleComponentBase instance.""" + super(ModuleComponentBase, self).__init__(parent=parent) + if parent: + self.component = self.component_class() self.name = name self.rationale = rationale self.buildorder = buildorder @@ -60,36 +71,56 @@ class ModuleComponentBase(object): @property def name(self): """A string property representing the component name.""" - return self._name + if self.parent: + return self.component.get_name() + else: + return self._name @name.setter def name(self, s): if not isinstance(s, str): raise TypeError("componentbase.name: data type not supported") - self._name = s + + if self.parent: + self.component.set_name(s) + else: + self._name = s @property def rationale(self): """A string property representing the rationale for the component inclusion in the module. """ - return self._rationale + if self.parent: + return self.component.get_rationale() + else: + return self._rationale @rationale.setter def rationale(self, s): if not isinstance(s, str): - raise TypeError("componentbase.rationale: data type not supported") - self._rationale = s + raise TypeError("componentbase.rationale: data type not supported" + " ({!r})".format(s)) + if self.parent: + self.component.set_rationale(s) + else: + self._rationale = s @property def buildorder(self): """An integer property representing the buildorder index for this component. """ - return self._buildorder + if self.parent: + return self.component.get_buildorder() + else: + return self._buildorder @buildorder.setter def buildorder(self, i): if not isinstance(i, integer_types): raise TypeError("componentbase.buildorder: data type not supported") - self._buildorder = i + if self.parent: + self.component.set_buildorder(i) + else: + self._buildorder = i diff --git a/modulemd/components/module.py b/modulemd/components/module.py index 31dbfc7..3052652 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -22,25 +22,44 @@ # # Written by Petr Šabata +from gi.repository import Modulemd + from .base import ModuleComponentBase class ModuleComponentModule(ModuleComponentBase): """A component class for handling module-type content.""" + tree_props = ('repository', 'ref') + + component_class = Modulemd.ComponentModule + def __init__(self, name, rationale, buildorder=0, - repository="", ref=""): + repository="", ref="", parent=None): """Creates a new ModuleComponentModule instance.""" - super(ModuleComponentModule, self).__init__(name, rationale, buildorder) + super(ModuleComponentModule, self).__init__(name=name, + rationale=rationale, + buildorder=buildorder, + parent=parent) self.repository = repository self.ref = ref @classmethod - def _new_from_native(cls, name, native_comp): - return cls(name, native_comp.get_rationale(), + def _new_from_native(cls, native_comp, parent=None): + return cls(native_comp.get_name(), native_comp.get_rationale(), buildorder=native_comp.get_buildorder() or 0, repository=native_comp.get_repository() or "", - ref=native_comp.get_ref() or "") + ref=native_comp.get_ref() or "", + parent=parent) + + def _to_native(self): + obj = self.component_class() + obj.set_name(self.name) + obj.set_rationale(self.rationale) + obj.set_buildorder(self.buildorder) + obj.set_repository(self.repository or None) + obj.set_ref(self.ref or None) + return obj def __repr__(self): return (" +from gi.repository import Modulemd + from .base import ModuleComponentBase class ModuleComponentRPM(ModuleComponentBase): """A component class for handling RPM content.""" + tree_props = ('repository', 'ref', 'cache', 'arches', 'multilib') + + component_class = Modulemd.ComponentRpm + def __init__(self, name, rationale, buildorder=0, repository="", ref="", cache="", arches=set(), - multilib=set()): + multilib=set(), parent=None): """Creates a new ModuleComponentRPM instance.""" - super(ModuleComponentRPM, self).__init__(name, rationale, buildorder) + super(ModuleComponentRPM, self).__init__(name=name, + rationale=rationale, + buildorder=buildorder, + parent=parent) self.repository = repository self.ref = ref self.cache = cache @@ -40,14 +49,35 @@ class ModuleComponentRPM(ModuleComponentBase): self.multilib = multilib @classmethod - def _new_from_native(cls, name, native_comp): - return cls(name, native_comp.get_rationale(), + def _new_from_native(cls, native_comp, parent=None): + return cls(name=native_comp.get_name(), + rationale=native_comp.get_rationale(), buildorder=native_comp.get_buildorder() or 0, repository=native_comp.get_repository() or "", ref=native_comp.get_ref() or "", cache=native_comp.get_cache() or "", arches=set(native_comp.get_arches().get()), - multilib=set(native_comp.get_multilib().get())) + multilib=set(native_comp.get_multilib().get()), + parent=parent) + + def _to_native(self): + obj = self.component_class() + obj.set_name(self.name) + obj.set_rationale(self.rationale) + obj.set_buildorder(self.buildorder) + obj.set_repository(self.repository or None) + obj.set_ref(self.ref or None) + obj.set_cache(self.cache or None) + + ss = Modulemd.SimpleSet() + ss.set(list(self.arches)) + obj.set_arches(ss) + + ss = Modulemd.SimpleSet() + ss.set(list(self.multilib)) + obj.set_multilib(ss) + + return obj def __repr__(self): return (" Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 38/48] migrate ModuleProfile to TreeObj --- diff --git a/modulemd/profile.py b/modulemd/profile.py index abd515a..275320e 100644 --- a/modulemd/profile.py +++ b/modulemd/profile.py @@ -22,27 +22,39 @@ # # Written by Petr Šabata +from gi.repository import Modulemd + +from .legacy_wrappers import TreeObj, RPMsMixin + + supported_content = ("rpms",) -from gi.repository import Modulemd -class ModuleProfile(object): +class ModuleProfile(TreeObj, RPMsMixin): """Class representing a particular module profile.""" - def __init__(self, profile=None, name=None, parent=None): + tree_props = ('name',) + + @property + def tree_identity(self): + return "profile<{}>".format(getattr(self, 'name', '')) + + def __init__(self, name=None, description=None, rpms=None, bound=False, + parent=None): """Creates a new ModuleProfile instance.""" - if profile: - if isinstance(profile, Modulemd.Profile): - self.profile = profile - else: - raise TypeError("Supplied value is not a profile") - else: + if parent: self.profile = Modulemd.Profile() + else: + assert not bound - if parent: - self.parent = parent - if name: - self.name = name + self.bound = False + + super(ModuleProfile, self).__init__(rpms=rpms, parent=parent) + + self.name = name + self.description = description + self.bound = bound + self._save_to_collection() def __repr__(self): return (" Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 39/48] use TreeObj/RPMsMixin based classes in ModuleMetadata --- diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 48d1ee9..6ed8119 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -659,12 +659,9 @@ class ModuleMetadata(object): @property def profiles(self): """A dictionary property representing the module profiles.""" - # TODO: return the profiles as a dict of ModuleProfile types - d = dict() - for k, v in self.module.get_profiles().items(): - d[k] = ModuleProfile(profile=v, parent=self, name=k) - - return d + return {k: ModuleProfile._new_from_native(native_profile=v, + bound=True, parent=self) + for k, v in self.module.get_profiles().items()} @profiles.setter def profiles(self, d): @@ -674,101 +671,82 @@ class ModuleMetadata(object): if not isinstance(k, str) or not isinstance(v, ModuleProfile): raise TypeError("profiles: data type not supported") - profiles = self.module.get_profiles() - + # ensure keys and names in profiles agree for k, v in d.items(): - profile = Modulemd.Profile() - if v.description: - profile.set_description(v.description) + v.name = k - ss = Modulemd.SimpleSet() - ss.set(list(v.rpms)) + self.module.set_profiles({k: v._to_native() for k, v in d.items()}) - profile.set_rpms(ss) - profiles[k] = profile - self.module.set_profiles(profiles) + @property + def tree_objs(self): + if not hasattr(self, '_tree_objs'): + self._tree_objs = { + 'api': ModuleAPI(parent=self), + 'filter': ModuleFilter(parent=self), + 'buildopts': ModuleBuildopts(parent=self), + 'components': ModuleComponents(parent=self), + 'artifacts': ModuleArtifacts(parent=self), + } + return self._tree_objs @property def api(self): """A ModuleAPI instance representing the module's public API.""" - api = ModuleAPI(parent=self) - api.rpms = set(self.module.get_rpm_api().get()) - return api + return self.tree_objs['api'] @api.setter def api(self, o): if not isinstance(o, ModuleAPI): raise TypeError("api: data type not supported") - rpms = Modulemd.SimpleSet() - rpms.set(list(o.rpms)) - - self.module.set_rpm_api(rpms) + self.tree_objs['api'].replace(o) @property def filter(self): """A ModuleFilter instance representing the module's filter.""" - filter = ModuleFilter(parent=self) - filter.rpms = set(self.module.get_rpm_filter().get()) - return filter + return self.tree_objs['filter'] @filter.setter def filter(self, o): if not isinstance(o, ModuleFilter): raise TypeError("filter: data type not supported") - rpms = Modulemd.SimpleSet() - rpms.set(list(o.rpms)) - - self.module.set_rpm_filter(rpms) + self.tree_objs['filter'].replace(o) @property def buildopts(self): """A ModuleBuildopts instance representing the additional module components build options. """ - if not getattr(self, '_buildopts', None): - self.buildopts = ModuleBuildopts(parent=self) - return self._buildopts + return self.tree_objs['buildopts'] @buildopts.setter def buildopts(self, o): if not isinstance(o, ModuleBuildopts): raise TypeError("buildopts: data type not supported") - self._buildopts = o + + self.tree_objs['buildopts'].replace(o) @property def components(self): """A ModuleComponents instance property representing the components defining the module. """ - if not hasattr(self, '_components'): - self._components = ModuleComponents(parent=self) - self._components.rpms = self.module.get_rpm_components() - self._components.modules = self.module.get_module_components() - return self._components + return self.tree_objs['components'] @components.setter def components(self, o): if not isinstance(o, ModuleComponents): raise TypeError("components: data type not supported") - - self.module.set_rpm_components(o.rpms) - self.module.set_module_components(o.modules) + self.tree_objs['components'].replace(o) @property def artifacts(self): """A ModuleArtifacts instance representing the module's artifacts.""" - artifacts = ModuleArtifacts(parent=self) - artifacts.rpms = set(self.module.get_rpm_artifacts().get()) - return artifacts + return self.tree_objs['artifacts'] @artifacts.setter def artifacts(self, o): if not isinstance(o, ModuleArtifacts): raise TypeError("artifacts: data type not supported") - - rpms = Modulemd.SimpleSet() - rpms.set(list(o.rpms)) - - self.module.set_rpm_artifacts(rpms) + self.tree_objs['artifacts'].replace(o) From a6ade1450f50bb57a339e8051fa9c7a9bc115f18 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 40/48] fix broken hash-bangs --- diff --git a/modulemd/tests/test_basic.py b/modulemd/tests/test_basic.py index 8875357..2c8d728 100644 --- a/modulemd/tests/test_basic.py +++ b/modulemd/tests/test_basic.py @@ -1,4 +1,4 @@ -#/usr/bin/python3 +#!/usr/bin/python3 # -*- coding: utf-8 -*- # Copyright © 2016 Red Hat, Inc. diff --git a/modulemd/tests/test_convenience.py b/modulemd/tests/test_convenience.py index dbdaf55..d2408fe 100644 --- a/modulemd/tests/test_convenience.py +++ b/modulemd/tests/test_convenience.py @@ -1,4 +1,4 @@ -#/usr/bin/python3 +#!/usr/bin/python3 # -*- coding: utf-8 -*- # Copyright © 2016 Red Hat, Inc. diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index 8bddd9f..f3ebba2 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -1,4 +1,4 @@ -#/usr/bin/python3 +#!/usr/bin/python3 # -*- coding: utf-8 -*- # Copyright © 2016 Red Hat, Inc. diff --git a/modulemd/tests/test_validation.py b/modulemd/tests/test_validation.py index 4b54a61..c7cac62 100644 --- a/modulemd/tests/test_validation.py +++ b/modulemd/tests/test_validation.py @@ -1,4 +1,4 @@ -#/usr/bin/python3 +#!/usr/bin/python3 # -*- coding: utf-8 -*- # Copyright © 2016 Red Hat, Inc. From ceb59ed5ba1e9a1d0c5d3244f4192787188f8b24 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 41/48] update copyright terms --- diff --git a/LICENSE b/LICENSE index 73c5ddb..c141775 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright © 2016 Red Hat, Inc. +Copyright © 2016 - 2018 Red Hat, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/modulemd/__init__.py b/modulemd/__init__.py index 6ed8119..9104ea6 100644 --- a/modulemd/__init__.py +++ b/modulemd/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/api.py b/modulemd/api.py index 29ce080..8c886da 100644 --- a/modulemd/api.py +++ b/modulemd/api.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/artifacts.py b/modulemd/artifacts.py index e575f2f..a0d984e 100644 --- a/modulemd/artifacts.py +++ b/modulemd/artifacts.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016, 2017 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/buildopts/__init__.py b/modulemd/buildopts/__init__.py index 9e7e06f..8a9a1d5 100644 --- a/modulemd/buildopts/__init__.py +++ b/modulemd/buildopts/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016, 2017 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/buildopts/base.py b/modulemd/buildopts/base.py index f82ac7e..091099a 100644 --- a/modulemd/buildopts/base.py +++ b/modulemd/buildopts/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016, 2017 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/buildopts/rpms.py b/modulemd/buildopts/rpms.py index 2ca97f7..01f4b91 100644 --- a/modulemd/buildopts/rpms.py +++ b/modulemd/buildopts/rpms.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016, 2017 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/components/__init__.py b/modulemd/components/__init__.py index 52c812a..614fe59 100644 --- a/modulemd/components/__init__.py +++ b/modulemd/components/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/components/base.py b/modulemd/components/base.py index bf02033..ad3fb59 100644 --- a/modulemd/components/base.py +++ b/modulemd/components/base.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/components/module.py b/modulemd/components/module.py index 3052652..c45b418 100644 --- a/modulemd/components/module.py +++ b/modulemd/components/module.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016, 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/components/rpm.py b/modulemd/components/rpm.py index d7f616e..c07e7cf 100644 --- a/modulemd/components/rpm.py +++ b/modulemd/components/rpm.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016, 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/filter.py b/modulemd/filter.py index c1e47a7..07a4fc3 100644 --- a/modulemd/filter.py +++ b/modulemd/filter.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/profile.py b/modulemd/profile.py index 275320e..c92c684 100644 --- a/modulemd/profile.py +++ b/modulemd/profile.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/tests/test_basic.py b/modulemd/tests/test_basic.py index 2c8d728..3a8adca 100644 --- a/modulemd/tests/test_basic.py +++ b/modulemd/tests/test_basic.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/tests/test_convenience.py b/modulemd/tests/test_convenience.py index d2408fe..3f01383 100644 --- a/modulemd/tests/test_convenience.py +++ b/modulemd/tests/test_convenience.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index f3ebba2..a87d237 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016, 2017 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal diff --git a/modulemd/tests/test_validation.py b/modulemd/tests/test_validation.py index c7cac62..6091db6 100644 --- a/modulemd/tests/test_validation.py +++ b/modulemd/tests/test_validation.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- -# Copyright © 2016 Red Hat, Inc. +# Copyright © 2016 - 2018 Red Hat, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From aac48db935a3f6a615f7c58e035fbc503e221498 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 42/48] move spec.yaml to tests and rename --- diff --git a/MANIFEST.in b/MANIFEST.in index a0bb9ba..4d52fe3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ recursive-include docs * include README.rst -include spec.yaml include LICENSE +include modulemd/tests/test.yaml diff --git a/modulemd/tests/test.yaml b/modulemd/tests/test.yaml new file mode 100644 index 0000000..6b5b66b --- /dev/null +++ b/modulemd/tests/test.yaml @@ -0,0 +1,282 @@ +# Document type identifier +document: modulemd +# Module metadata format version +version: 1 +data: + # Module name, optional + # Typically filled in by the buildsystem, using the VCS repository + # name as the name of the module. + name: foo + # Module update stream, optional + # Typically filled in by the buildsystem, using the VCS branch name + # as the name of the stream. + stream: stream-name + # Module version, integer, optional, cannot be negative + # Typically filled in by the buildsystem, using the VCS commit + # timestamp. Module version defines upgrade path for the particular + # update stream. + version: 20160927144203 + # Module context flag, optional + # The context flag serves to distinguish module builds with the + # same name, stream and version and plays an important role in + # future automatic module stream name expansion. + # Filled in by the buildsystem. A short hash of the module's name, + # stream, version and its expanded runtime dependencies. + context: c0ffee43 + # Module artifact architecture, optional + # Contains a string describing the module's artifacts' main hardware + # architecture compatibility, distinguishing the module artifact, + # e.g. a repository, from others with the same name, stream, version and + # context. This is not a generic hardware family (i.e. basearch). + # Examples: i386, i486, armv7hl, x86_64 + # Filled in by the buildsystem during the compose stage. + arch: x86_64 + # A short summary describing the module, required + summary: An example module + # A verbose description of the module, required + description: >- + A module for the demonstration of the metadata format. Also, + the obligatory lorem ipsum dolor sit amet goes right here. + # The end of life, aka Best Before, optional. + # A UTC date in the ISO 8601 format signifying the day when this + # module goes EOL, i.e. starting the day below, this module won't + # receive any more updates. Typically defined in an external data + # source and filled in by the buildsystem. + eol: 2077-10-23 + # Service levels, optional + # This is a dictionary of important dates (and possibly supplementary data + # in the future) that describes the end point of certain functionality, + # such as the date when the module will transition to "security fixes only" + # or go completely end-of-life + servicelevels: + security: + eol: 2019-03-30 + features: + eol: 2018-12-31 + # Module and content licenses in the Fedora license identifier + # format, required + license: + # Module license, required + # This list covers licenses used for the module metadata and + # possibly other files involved in the creation of this specific + # module. + module: + - MIT + # Content license, optional + # A list of licenses used by the packages in the module. + # This should be populated by build tools, not the module author. + content: + - Beerware + - GPLv2+ + - zlib + # Extensible metadata block + # A dictionary of user-defined keys and values. + # Optional. Defaults to an empty dictionary. + xmd: + some_key: some_data + # Module dependencies, if any. Optional. + # TODO: Provides, conflicts, obsoletes, recommends, etc. + # Do we even need those? + # TODO: Stream name globbing or regular expression support + dependencies: + # Build dependencies of this module, optional + # Keys are module names, values are the stream names + # These modules define the buildroot for this module + buildrequires: + platform: and-its-stream-name + extra-build-env: and-its-stream-name-too + # Run-time dependencies of this module, optional + # Keys are module names, values are their stream names + requires: + platform: and-its-stream-name + # References to external resources, typically upstream, optional + references: + # Upstream community website, if it exists, optional + community: http://www.example.com/ + # Upstream documentation, if it exists, optional + documentation: http://www.example.com/ + # Upstream bug tracker, if it exists, optional + tracker: http://www.example.com/ + # Profiles define the end user's use cases for the module. They consist of + # package lists of components to be installed by default if the module is + # enabled. The keys are the profile names and contain package lists by + # component type. There are several profiles defined below. Suggested + # behavior for package managers is to just enable repository for selected + # module. Then users are able to install packages on their own. If they + # select a specific profile, the package manager should install all + # packages of that profile. + # Optional, defaults to no profile definitions. + profiles: + # The default profile, used unless any other profile was selected. + # Optional, defaults to empty lists. + default: + rpms: + - bar + - bar-extras + - baz + # Defines a set of packages which are meant to be installed inside + # container image artifact. + # Optional. + container: + rpms: + - bar + - bar-devel + # This profile provides minimal set of packages providing functionality + # of this module. This is meant to be used on target systems where size + # of the distribution is a real concern. + # Optional. + minimal: + # A verbose description of the module, optional + description: Minimal profile installing only the bar package. + rpms: + - bar + # A set of packages which should be installed into the buildroot of a + # module which depends on this module. Specifically, it is used to + # flesh out the build group in koji. + # Optional. + buildroot: + rpms: + - bar-devel + # Very similar to the buildroot profile above, this is used by the + # build system to specify any additional packages which should be + # installed during the buildSRPMfromSCM step in koji. + # Optional. + srpm-buildroot: + rpms: + - bar-extras + # Module API + # Optional, defaults to no API. + api: + # The module's public RPM-level API. + # A list of binary RPM names that are considered to be the + # main and stable feature of the module; binary RPMs not listed + # here are considered "unsupported" or "implementation details". + # In the example here we don't list the xyz package as it's only + # included as a dependency of xxx. However, we list a subpackage + # of bar, bar-extras. + # Optional, defaults to an empty list. + rpms: + - bar + - bar-extras + - bar-devel + - baz + - xxx + # Module component filters + # Optional, defaults to no filters. + filter: + # RPM names not to be included in the module. + # By default, all built binary RPMs are included. In the example + # we exclude a subpackage of bar, bar-nonfoo from our module. + # Optional, defaults to an empty list. + rpms: + - baz-nonfoo + # Component build options + # Additional per component type module-wide build options. + # Optional + buildopts: + # RPM-specific build options + # Optional + rpms: + # Additional macros that should be defined in the + # RPM buildroot, appended to the default set. Care should be + # taken so that the newlines are preserved. Literal style + # block is recommended, with or without the trailing newline. + # Optional + macros: | + %demomacro 1 + %demomacro2 %{demomacro}23 + # Functional components of the module, optional + components: + # RPM content of the module, optional + # Keys are the VCS/SRPM names, values dictionaries holding + # additional information. + rpms: + bar: + # Why is this component present. + # A simple, free-form string. + # Required. + rationale: We need this to demonstrate stuff. + # Use this repository if it's different from the build + # system configuration. + # Optional. + repository: https://pagure.io/bar.git + # Use this lookaside cache if it's different from the + # build system configuration. + # Optional. + cache: https://example.com/cache + # Use this specific commit has, branch name or tag for + # the build. If ref is a branch name, the branch HEAD + # will be used. If no ref is given, the master branch + # is assumed. + # Optional. + ref: 26ca0c0 + # baz has no extra options + baz: + rationale: This one is here to demonstrate other stuff. + xxx: + rationale: xxx demonstrates arches and multilib. + # xxx is only available on the listed architectures. + # TODO: This needs a better definition of what the + # architectures here actually mean. + # Optional, defaults to all available arches. + arches: [ i686, x86_64 ] + # A list of architectures with multilib + # installs, i.e. both i686 and x86_64 + # versions will be installed on x86_64. + # TODO: This needs to be reworked or dropped as it's + # not at all useful in the current state. + # Optional, defaults to no multilib. + multilib: [ x86_64 ] + xyz: + rationale: xyz is a bundled dependency of xxx. + # Build order group + # When building, components are sorted by build order tag + # and built in batches grouped by their buildorder value. + # Built batches are then re-tagged into the buildroot. + # Multiple components can have the same buildorder index + # to map them into build groups. + # Optional, defaults to zero. + # Integer, negative values are allowed. + # In this example, bar, baz and xxx are built first in + # no particular order, then tagged into the buildroot, + # then, finally, xyz is built. + buildorder: 10 + # Module content of this module + # Included modules are built in the shared buildroot, together with + # other included content. Keys are module names, values additional + # component information. Note this only includes components and their + # properties from the referenced module and doesn't inherit any + # additional module metadata such as the module's dependencies or + # component buildopts. The included components are built in their + # defined buildorder as sub-build groups. + # Optional + modules: + includedmodule: + # Why is this module included? + # Required + rationale: Included in the stack, just because. + # Link to VCS repository that contains the modulemd file + # if it differs from the buildsystem default configuration. + # Optional. + repository: https://pagure.io/includedmodule.git + # See the rpms ref. + ref: somecoolbranchname + # See the rpms buildorder. + buildorder: 100 + # Artifacts shipped with this module + # This section lists binary artifacts shipped with the module, allowing + # software management tools to handle module bundles. This section is + # populated by the module build system. + # Optional + artifacts: + # RPM artifacts shipped with this module + # A set of NEVRAs associated with this module. + # Optional + rpms: + - bar-0:1.23-1.module_deadbeef.x86_64 + - bar-devel-0:1.23-1.module_deadbeef.x86_64 + - bar-extras-0:1.23-1.module_deadbeef.x86_64 + - baz-0:42-42.module_deadbeef.x86_64 + - xxx-0:1-1.module_deadbeef.x86_64 + - xxx-0:1-1.module_deadbeef.i686 + - xyz-0:1-1.module_deadbeef.x86_64 diff --git a/spec.yaml b/spec.yaml deleted file mode 100644 index 6b5b66b..0000000 --- a/spec.yaml +++ /dev/null @@ -1,282 +0,0 @@ -# Document type identifier -document: modulemd -# Module metadata format version -version: 1 -data: - # Module name, optional - # Typically filled in by the buildsystem, using the VCS repository - # name as the name of the module. - name: foo - # Module update stream, optional - # Typically filled in by the buildsystem, using the VCS branch name - # as the name of the stream. - stream: stream-name - # Module version, integer, optional, cannot be negative - # Typically filled in by the buildsystem, using the VCS commit - # timestamp. Module version defines upgrade path for the particular - # update stream. - version: 20160927144203 - # Module context flag, optional - # The context flag serves to distinguish module builds with the - # same name, stream and version and plays an important role in - # future automatic module stream name expansion. - # Filled in by the buildsystem. A short hash of the module's name, - # stream, version and its expanded runtime dependencies. - context: c0ffee43 - # Module artifact architecture, optional - # Contains a string describing the module's artifacts' main hardware - # architecture compatibility, distinguishing the module artifact, - # e.g. a repository, from others with the same name, stream, version and - # context. This is not a generic hardware family (i.e. basearch). - # Examples: i386, i486, armv7hl, x86_64 - # Filled in by the buildsystem during the compose stage. - arch: x86_64 - # A short summary describing the module, required - summary: An example module - # A verbose description of the module, required - description: >- - A module for the demonstration of the metadata format. Also, - the obligatory lorem ipsum dolor sit amet goes right here. - # The end of life, aka Best Before, optional. - # A UTC date in the ISO 8601 format signifying the day when this - # module goes EOL, i.e. starting the day below, this module won't - # receive any more updates. Typically defined in an external data - # source and filled in by the buildsystem. - eol: 2077-10-23 - # Service levels, optional - # This is a dictionary of important dates (and possibly supplementary data - # in the future) that describes the end point of certain functionality, - # such as the date when the module will transition to "security fixes only" - # or go completely end-of-life - servicelevels: - security: - eol: 2019-03-30 - features: - eol: 2018-12-31 - # Module and content licenses in the Fedora license identifier - # format, required - license: - # Module license, required - # This list covers licenses used for the module metadata and - # possibly other files involved in the creation of this specific - # module. - module: - - MIT - # Content license, optional - # A list of licenses used by the packages in the module. - # This should be populated by build tools, not the module author. - content: - - Beerware - - GPLv2+ - - zlib - # Extensible metadata block - # A dictionary of user-defined keys and values. - # Optional. Defaults to an empty dictionary. - xmd: - some_key: some_data - # Module dependencies, if any. Optional. - # TODO: Provides, conflicts, obsoletes, recommends, etc. - # Do we even need those? - # TODO: Stream name globbing or regular expression support - dependencies: - # Build dependencies of this module, optional - # Keys are module names, values are the stream names - # These modules define the buildroot for this module - buildrequires: - platform: and-its-stream-name - extra-build-env: and-its-stream-name-too - # Run-time dependencies of this module, optional - # Keys are module names, values are their stream names - requires: - platform: and-its-stream-name - # References to external resources, typically upstream, optional - references: - # Upstream community website, if it exists, optional - community: http://www.example.com/ - # Upstream documentation, if it exists, optional - documentation: http://www.example.com/ - # Upstream bug tracker, if it exists, optional - tracker: http://www.example.com/ - # Profiles define the end user's use cases for the module. They consist of - # package lists of components to be installed by default if the module is - # enabled. The keys are the profile names and contain package lists by - # component type. There are several profiles defined below. Suggested - # behavior for package managers is to just enable repository for selected - # module. Then users are able to install packages on their own. If they - # select a specific profile, the package manager should install all - # packages of that profile. - # Optional, defaults to no profile definitions. - profiles: - # The default profile, used unless any other profile was selected. - # Optional, defaults to empty lists. - default: - rpms: - - bar - - bar-extras - - baz - # Defines a set of packages which are meant to be installed inside - # container image artifact. - # Optional. - container: - rpms: - - bar - - bar-devel - # This profile provides minimal set of packages providing functionality - # of this module. This is meant to be used on target systems where size - # of the distribution is a real concern. - # Optional. - minimal: - # A verbose description of the module, optional - description: Minimal profile installing only the bar package. - rpms: - - bar - # A set of packages which should be installed into the buildroot of a - # module which depends on this module. Specifically, it is used to - # flesh out the build group in koji. - # Optional. - buildroot: - rpms: - - bar-devel - # Very similar to the buildroot profile above, this is used by the - # build system to specify any additional packages which should be - # installed during the buildSRPMfromSCM step in koji. - # Optional. - srpm-buildroot: - rpms: - - bar-extras - # Module API - # Optional, defaults to no API. - api: - # The module's public RPM-level API. - # A list of binary RPM names that are considered to be the - # main and stable feature of the module; binary RPMs not listed - # here are considered "unsupported" or "implementation details". - # In the example here we don't list the xyz package as it's only - # included as a dependency of xxx. However, we list a subpackage - # of bar, bar-extras. - # Optional, defaults to an empty list. - rpms: - - bar - - bar-extras - - bar-devel - - baz - - xxx - # Module component filters - # Optional, defaults to no filters. - filter: - # RPM names not to be included in the module. - # By default, all built binary RPMs are included. In the example - # we exclude a subpackage of bar, bar-nonfoo from our module. - # Optional, defaults to an empty list. - rpms: - - baz-nonfoo - # Component build options - # Additional per component type module-wide build options. - # Optional - buildopts: - # RPM-specific build options - # Optional - rpms: - # Additional macros that should be defined in the - # RPM buildroot, appended to the default set. Care should be - # taken so that the newlines are preserved. Literal style - # block is recommended, with or without the trailing newline. - # Optional - macros: | - %demomacro 1 - %demomacro2 %{demomacro}23 - # Functional components of the module, optional - components: - # RPM content of the module, optional - # Keys are the VCS/SRPM names, values dictionaries holding - # additional information. - rpms: - bar: - # Why is this component present. - # A simple, free-form string. - # Required. - rationale: We need this to demonstrate stuff. - # Use this repository if it's different from the build - # system configuration. - # Optional. - repository: https://pagure.io/bar.git - # Use this lookaside cache if it's different from the - # build system configuration. - # Optional. - cache: https://example.com/cache - # Use this specific commit has, branch name or tag for - # the build. If ref is a branch name, the branch HEAD - # will be used. If no ref is given, the master branch - # is assumed. - # Optional. - ref: 26ca0c0 - # baz has no extra options - baz: - rationale: This one is here to demonstrate other stuff. - xxx: - rationale: xxx demonstrates arches and multilib. - # xxx is only available on the listed architectures. - # TODO: This needs a better definition of what the - # architectures here actually mean. - # Optional, defaults to all available arches. - arches: [ i686, x86_64 ] - # A list of architectures with multilib - # installs, i.e. both i686 and x86_64 - # versions will be installed on x86_64. - # TODO: This needs to be reworked or dropped as it's - # not at all useful in the current state. - # Optional, defaults to no multilib. - multilib: [ x86_64 ] - xyz: - rationale: xyz is a bundled dependency of xxx. - # Build order group - # When building, components are sorted by build order tag - # and built in batches grouped by their buildorder value. - # Built batches are then re-tagged into the buildroot. - # Multiple components can have the same buildorder index - # to map them into build groups. - # Optional, defaults to zero. - # Integer, negative values are allowed. - # In this example, bar, baz and xxx are built first in - # no particular order, then tagged into the buildroot, - # then, finally, xyz is built. - buildorder: 10 - # Module content of this module - # Included modules are built in the shared buildroot, together with - # other included content. Keys are module names, values additional - # component information. Note this only includes components and their - # properties from the referenced module and doesn't inherit any - # additional module metadata such as the module's dependencies or - # component buildopts. The included components are built in their - # defined buildorder as sub-build groups. - # Optional - modules: - includedmodule: - # Why is this module included? - # Required - rationale: Included in the stack, just because. - # Link to VCS repository that contains the modulemd file - # if it differs from the buildsystem default configuration. - # Optional. - repository: https://pagure.io/includedmodule.git - # See the rpms ref. - ref: somecoolbranchname - # See the rpms buildorder. - buildorder: 100 - # Artifacts shipped with this module - # This section lists binary artifacts shipped with the module, allowing - # software management tools to handle module bundles. This section is - # populated by the module build system. - # Optional - artifacts: - # RPM artifacts shipped with this module - # A set of NEVRAs associated with this module. - # Optional - rpms: - - bar-0:1.23-1.module_deadbeef.x86_64 - - bar-devel-0:1.23-1.module_deadbeef.x86_64 - - bar-extras-0:1.23-1.module_deadbeef.x86_64 - - baz-0:42-42.module_deadbeef.x86_64 - - xxx-0:1-1.module_deadbeef.x86_64 - - xxx-0:1-1.module_deadbeef.i686 - - xyz-0:1-1.module_deadbeef.x86_64 From e6d9f83793b40e0b8c31abbda2996b35c1c21e66 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 43/48] remove spec comments and add explanation --- diff --git a/modulemd/tests/test.yaml b/modulemd/tests/test.yaml index 6b5b66b..1421862 100644 --- a/modulemd/tests/test.yaml +++ b/modulemd/tests/test.yaml @@ -1,277 +1,99 @@ -# Document type identifier +# This file merely contains test data, don't use it as a reference. document: modulemd -# Module metadata format version version: 1 data: - # Module name, optional - # Typically filled in by the buildsystem, using the VCS repository - # name as the name of the module. name: foo - # Module update stream, optional - # Typically filled in by the buildsystem, using the VCS branch name - # as the name of the stream. stream: stream-name - # Module version, integer, optional, cannot be negative - # Typically filled in by the buildsystem, using the VCS commit - # timestamp. Module version defines upgrade path for the particular - # update stream. version: 20160927144203 - # Module context flag, optional - # The context flag serves to distinguish module builds with the - # same name, stream and version and plays an important role in - # future automatic module stream name expansion. - # Filled in by the buildsystem. A short hash of the module's name, - # stream, version and its expanded runtime dependencies. context: c0ffee43 - # Module artifact architecture, optional - # Contains a string describing the module's artifacts' main hardware - # architecture compatibility, distinguishing the module artifact, - # e.g. a repository, from others with the same name, stream, version and - # context. This is not a generic hardware family (i.e. basearch). - # Examples: i386, i486, armv7hl, x86_64 - # Filled in by the buildsystem during the compose stage. arch: x86_64 - # A short summary describing the module, required summary: An example module - # A verbose description of the module, required description: >- A module for the demonstration of the metadata format. Also, the obligatory lorem ipsum dolor sit amet goes right here. - # The end of life, aka Best Before, optional. - # A UTC date in the ISO 8601 format signifying the day when this - # module goes EOL, i.e. starting the day below, this module won't - # receive any more updates. Typically defined in an external data - # source and filled in by the buildsystem. eol: 2077-10-23 - # Service levels, optional - # This is a dictionary of important dates (and possibly supplementary data - # in the future) that describes the end point of certain functionality, - # such as the date when the module will transition to "security fixes only" - # or go completely end-of-life servicelevels: security: eol: 2019-03-30 features: eol: 2018-12-31 - # Module and content licenses in the Fedora license identifier - # format, required license: - # Module license, required - # This list covers licenses used for the module metadata and - # possibly other files involved in the creation of this specific - # module. module: - MIT - # Content license, optional - # A list of licenses used by the packages in the module. - # This should be populated by build tools, not the module author. content: - Beerware - GPLv2+ - zlib - # Extensible metadata block - # A dictionary of user-defined keys and values. - # Optional. Defaults to an empty dictionary. xmd: some_key: some_data - # Module dependencies, if any. Optional. - # TODO: Provides, conflicts, obsoletes, recommends, etc. - # Do we even need those? - # TODO: Stream name globbing or regular expression support dependencies: - # Build dependencies of this module, optional - # Keys are module names, values are the stream names - # These modules define the buildroot for this module buildrequires: platform: and-its-stream-name extra-build-env: and-its-stream-name-too - # Run-time dependencies of this module, optional - # Keys are module names, values are their stream names requires: platform: and-its-stream-name - # References to external resources, typically upstream, optional references: - # Upstream community website, if it exists, optional community: http://www.example.com/ - # Upstream documentation, if it exists, optional documentation: http://www.example.com/ - # Upstream bug tracker, if it exists, optional tracker: http://www.example.com/ - # Profiles define the end user's use cases for the module. They consist of - # package lists of components to be installed by default if the module is - # enabled. The keys are the profile names and contain package lists by - # component type. There are several profiles defined below. Suggested - # behavior for package managers is to just enable repository for selected - # module. Then users are able to install packages on their own. If they - # select a specific profile, the package manager should install all - # packages of that profile. - # Optional, defaults to no profile definitions. profiles: - # The default profile, used unless any other profile was selected. - # Optional, defaults to empty lists. default: rpms: - bar - bar-extras - baz - # Defines a set of packages which are meant to be installed inside - # container image artifact. - # Optional. container: rpms: - bar - bar-devel - # This profile provides minimal set of packages providing functionality - # of this module. This is meant to be used on target systems where size - # of the distribution is a real concern. - # Optional. minimal: - # A verbose description of the module, optional description: Minimal profile installing only the bar package. rpms: - bar - # A set of packages which should be installed into the buildroot of a - # module which depends on this module. Specifically, it is used to - # flesh out the build group in koji. - # Optional. buildroot: rpms: - bar-devel - # Very similar to the buildroot profile above, this is used by the - # build system to specify any additional packages which should be - # installed during the buildSRPMfromSCM step in koji. - # Optional. srpm-buildroot: rpms: - bar-extras - # Module API - # Optional, defaults to no API. api: - # The module's public RPM-level API. - # A list of binary RPM names that are considered to be the - # main and stable feature of the module; binary RPMs not listed - # here are considered "unsupported" or "implementation details". - # In the example here we don't list the xyz package as it's only - # included as a dependency of xxx. However, we list a subpackage - # of bar, bar-extras. - # Optional, defaults to an empty list. rpms: - bar - bar-extras - bar-devel - baz - xxx - # Module component filters - # Optional, defaults to no filters. filter: - # RPM names not to be included in the module. - # By default, all built binary RPMs are included. In the example - # we exclude a subpackage of bar, bar-nonfoo from our module. - # Optional, defaults to an empty list. rpms: - baz-nonfoo - # Component build options - # Additional per component type module-wide build options. - # Optional buildopts: - # RPM-specific build options - # Optional rpms: - # Additional macros that should be defined in the - # RPM buildroot, appended to the default set. Care should be - # taken so that the newlines are preserved. Literal style - # block is recommended, with or without the trailing newline. - # Optional macros: | %demomacro 1 %demomacro2 %{demomacro}23 - # Functional components of the module, optional components: - # RPM content of the module, optional - # Keys are the VCS/SRPM names, values dictionaries holding - # additional information. rpms: bar: - # Why is this component present. - # A simple, free-form string. - # Required. rationale: We need this to demonstrate stuff. - # Use this repository if it's different from the build - # system configuration. - # Optional. repository: https://pagure.io/bar.git - # Use this lookaside cache if it's different from the - # build system configuration. - # Optional. cache: https://example.com/cache - # Use this specific commit has, branch name or tag for - # the build. If ref is a branch name, the branch HEAD - # will be used. If no ref is given, the master branch - # is assumed. - # Optional. ref: 26ca0c0 - # baz has no extra options baz: rationale: This one is here to demonstrate other stuff. xxx: rationale: xxx demonstrates arches and multilib. - # xxx is only available on the listed architectures. - # TODO: This needs a better definition of what the - # architectures here actually mean. - # Optional, defaults to all available arches. arches: [ i686, x86_64 ] - # A list of architectures with multilib - # installs, i.e. both i686 and x86_64 - # versions will be installed on x86_64. - # TODO: This needs to be reworked or dropped as it's - # not at all useful in the current state. - # Optional, defaults to no multilib. multilib: [ x86_64 ] xyz: rationale: xyz is a bundled dependency of xxx. - # Build order group - # When building, components are sorted by build order tag - # and built in batches grouped by their buildorder value. - # Built batches are then re-tagged into the buildroot. - # Multiple components can have the same buildorder index - # to map them into build groups. - # Optional, defaults to zero. - # Integer, negative values are allowed. - # In this example, bar, baz and xxx are built first in - # no particular order, then tagged into the buildroot, - # then, finally, xyz is built. buildorder: 10 - # Module content of this module - # Included modules are built in the shared buildroot, together with - # other included content. Keys are module names, values additional - # component information. Note this only includes components and their - # properties from the referenced module and doesn't inherit any - # additional module metadata such as the module's dependencies or - # component buildopts. The included components are built in their - # defined buildorder as sub-build groups. - # Optional modules: includedmodule: - # Why is this module included? - # Required rationale: Included in the stack, just because. - # Link to VCS repository that contains the modulemd file - # if it differs from the buildsystem default configuration. - # Optional. repository: https://pagure.io/includedmodule.git - # See the rpms ref. ref: somecoolbranchname - # See the rpms buildorder. buildorder: 100 - # Artifacts shipped with this module - # This section lists binary artifacts shipped with the module, allowing - # software management tools to handle module bundles. This section is - # populated by the module build system. - # Optional artifacts: - # RPM artifacts shipped with this module - # A set of NEVRAs associated with this module. - # Optional rpms: - bar-0:1.23-1.module_deadbeef.x86_64 - bar-devel-0:1.23-1.module_deadbeef.x86_64 diff --git a/modulemd/tests/test_io.py b/modulemd/tests/test_io.py index a87d237..0ef3ff4 100644 --- a/modulemd/tests/test_io.py +++ b/modulemd/tests/test_io.py @@ -34,6 +34,9 @@ sys.path.insert(0, os.path.join(DIR, "..")) from modulemd import ModuleMetadata, dump_all, load_all +TEST_YAML = os.path.join(DIR, "test.yaml") + + class _TestIOBase(unittest.TestCase): maxDiff = None # display diff when a test fails @@ -45,7 +48,7 @@ class _TestIOBase(unittest.TestCase): class TestIOMMDLoads(_TestIOBase): def test_load_spec(self): - self.mmd.load("spec.yaml") + self.mmd.load(TEST_YAML) class TestIO(_TestIOBase): @@ -53,7 +56,7 @@ class TestIO(_TestIOBase): @classmethod def setUpClass(cls): super(TestIO, cls).setUpClass() - cls.mmd.load("spec.yaml") + cls.mmd.load(TEST_YAML) def test_mdversion(self): self.assertEqual(self.mmd.mdversion, 1) @@ -194,7 +197,7 @@ class TestIO(_TestIOBase): "%demomacro 1\n%demomacro2 %{demomacro}23\n") def test_reload(self): - self.mmd.load("spec.yaml") + self.mmd.load(TEST_YAML) first = repr(self.mmd) self.mmd.dump("testdump.yaml") self.mmd = ModuleMetadata() From 5efcca3643ccd3a24c090ccd8408bce4e904f46c Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:37 +0000 Subject: [PATCH 44/48] remove RPM spec file --- diff --git a/package/modulemd.spec b/package/modulemd.spec deleted file mode 100644 index 0d0e7db..0000000 --- a/package/modulemd.spec +++ /dev/null @@ -1,144 +0,0 @@ -%global _pkgdescription A python library for manipulation of the proposed module metadata format. - -%if 0%{?fedora} > 21 || 0%{?rhel} > 7 -%global with_python3 1 -%else -%global with_python3 0 -%endif - -%{!?__python2: %global __python2 /usr/bin/python2} -%{!?py2_build: %global py2_build %{expand: CFLAGS="%{optflags}" %{__python2} setup.py %{?py_setup_args} build --executable="%{__python2} -s"}} -%{!?py2_install: %global py2_install %{expand: CFLAGS="%{optflags}" %{__python2} setup.py %{?py_setup_args} install -O1 --skip-build --root %{buildroot}}} - -Name: modulemd -Version: 1.1.0 -Release: 1%{?dist} -Summary: Module metadata manipulation library -License: MIT -URL: https://pagure.io/modulemd -Source0: https://files.pythonhosted.org/packages/source/m/%{name}/%{name}-%{version}.tar.gz -BuildArch: noarch -BuildRequires: python2-devel -BuildRequires: PyYAML -BuildRequires: python-setuptools -%if 0%{?with_python3} -BuildRequires: python3-devel -BuildRequires: python3-PyYAML -BuildRequires: python3-setuptools -%endif - -%description -%{_pkgdescription} - -%package -n python2-%{name} -Summary: %{summary} -Requires: PyYAML -Provides: python-%{name} = %{version}-%{release} - -%description -n python2-%{name} -%{_pkgdescription} - -These are python2 bindings. - -%if 0%{?with_python3} -%package -n python3-%{name} -Summary: %{summary} -Requires: python3-PyYAML - -%description -n python3-%{name} -%{_pkgdescription} - -These are python3 bindings. -%endif - -%prep -%setup -q - -%build -%py2_build -%if 0%{?with_python3} -%py3_build -%endif - -%install -%py2_install -%if 0%{?with_python3} -%py3_install -%endif - -%check -%{__python2} setup.py test -%if 0%{?with_python3} -%{__python3} setup.py test -%endif - -%files -n python2-%{name} -%doc README.rst spec.yaml -%license LICENSE -%{python2_sitelib}/* - -%if 0%{?with_python3} -%files -n python3-%{name} -%doc README.rst spec.yaml -%license LICENSE -%{python3_sitelib}/* -%endif - -%changelog -* Mon Feb 13 2017 Petr Šabata - 1.1.0-1 -- 1.1.0 bump - -* Tue Nov 08 2016 Petr Šabata - 1.0.2-1 -- 1.0.2 bugfix release - -* Tue Nov 08 2016 Petr Šabata - 1.0.1-1 -- First official release - -* Fri Sep 23 2016 Jan Kaluza - 0-13 -- build without Python 3 support on older distributions that don't have it - -* Fri Sep 16 2016 Petr Šabata - 0-12 -- Update modlint's runtime dependencies -- modlint shouldn't install the README and spec.yaml files - -* Wed Aug 03 2016 Jan Kaluza - 0-11 -- Add modlint subpackage - -* Tue Jul 19 2016 Petr Šabata - 0-10 -- Don't fail validation tests -- Use safe_dump() for dumping YAMLs - -* Tue Jul 12 2016 Petr Šabata - 0-9 -- Profiles now support description -- The components section is now truly optional - -* Sat Jul 09 2016 Petr Šabata - 0-8 -- rpms.update_package() now allows updating just one property - -* Thu Jun 30 2016 Petr Šabata - 0-7 -- Adding support for binary package filters - -* Tue Jun 21 2016 Petr Šabata - 0-6 -- New metadata format - - module use-case profiles are now supported - -* Tue Jun 14 2016 Petr Šabata - 0-5 -- Rename metadata.yaml to spec.yaml - -* Tue Jun 14 2016 Petr Šabata - 0-4 -- New metadata format - - rpms/api now holds the module RPM-defined API - -* Fri Jun 10 2016 Petr Šabata - 0-3 -- New metadata format - - rpms/dependencies defaults to False - - rpms/fulltree was removed - -* Thu May 12 2016 Petr Šabata - 0-2 -- New metadata format, rationale is now required - -* Fri May 06 2016 Petr Šabata - 0-1 -- New metadata format - -* Mon May 02 2016 Petr Šabata - 0-0 -- This package was build automatically. From 767aea4bd4b7ca4df97a3365405ed2e4f31dcb2b Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:38 +0000 Subject: [PATCH 45/48] document that modulemd is a wrapper --- diff --git a/README.rst b/README.rst index 49abe66..978de41 100644 --- a/README.rst +++ b/README.rst @@ -1,19 +1,13 @@ -Module metadata definitions and the modulemd library -==================================================== +Python wrapper around libmodulemd +================================= -This repository contains simple module metadata template and the corresponding -library for the manipulation thereof. - -`spec.yaml `_: - This file serves two roles -- it is the input for tools generating the - actual module (such as pungi-modularization) and it is also present in - the resulting repository, available to its consumers (such as - fm-metadata-service). For practical reasons, it is written in YAML. - See comments in the template for details. +This repository contains a Python wrapper around +`libmodulemd `_: modulemd: - A python library for manipulation of the proposed module metadata format. - API documentation is available at http://modulemd.readthedocs.org/. + A Python wrapper around the libmodulemd library for manipulation of the + module metadata format. API documentation is available at + http://modulemd.readthedocs.org/. Testing ------- From 2e8a54aaa425c2145f26a8b00cf103373cc317f5 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 23 2018 16:41:38 +0000 Subject: [PATCH 46/48] using tox for testing doesn't work With modulemd as the Python wrapper around libmodulemd (using gobject introspection), tox as it is configured fails to set up the virtualenv properly. Don't mention it in the README and remove its configuration. --- diff --git a/README.rst b/README.rst index 978de41..b976f00 100644 --- a/README.rst +++ b/README.rst @@ -8,12 +8,3 @@ modulemd: A Python wrapper around the libmodulemd library for manipulation of the module metadata format. API documentation is available at http://modulemd.readthedocs.org/. - -Testing -------- - -.. code-block:: bash - - $ tox -e py27,py35 - -*See tox.ini for additional environments.* diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 410db77..0000000 --- a/tox.ini +++ /dev/null @@ -1,41 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = py27, py35, coverage, flake8, bandit - -[flake8] -max-line-length = 100 - -[testenv] -usedevelop = true -deps = pytest -commands = - py.test {posargs} - -[testenv:coverage] -basepython = python2 -deps = - {[testenv]deps} - coverage -commands = - coverage run --parallel-mode -m pytest - coverage combine - coverage report --omit=.tox/* -m --skip-covered - -[testenv:flake8] -basepython = python3 -skip_install = true -deps = flake8 -commands = flake8 --ignore E731 --exclude .tox,.git -ignore_outcome = True - -[testenv:bandit] -basepython = python3 -skip_install = true -deps = bandit -commands = - /bin/bash -c "bandit -r -ll $(find . -mindepth 1 -maxdepth 1 ! -name \.\* -type d -o -name \*.py)" -ignore_outcome = True From d00fd31f61eacfc507f873fffe6d30f3403e20f9 Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 24 2018 13:08:58 +0000 Subject: [PATCH 47/48] instead of tox, describe running unit tests at least --- diff --git a/README.rst b/README.rst index b976f00..bcf07b6 100644 --- a/README.rst +++ b/README.rst @@ -8,3 +8,10 @@ modulemd: A Python wrapper around the libmodulemd library for manipulation of the module metadata format. API documentation is available at http://modulemd.readthedocs.org/. + +Testing +------- + +.. code-block:: bash + + $ python setup.py test From 775984542f6cd5a991040fbe4b5c9fc228bc79db Mon Sep 17 00:00:00 2001 From: Nils Philippsen Date: Jan 24 2018 13:08:58 +0000 Subject: [PATCH 48/48] link libmodulemd spec.yaml --- diff --git a/README.rst b/README.rst index bcf07b6..351bc8d 100644 --- a/README.rst +++ b/README.rst @@ -9,6 +9,11 @@ modulemd: module metadata format. API documentation is available at http://modulemd.readthedocs.org/. +The ``spec.yaml`` file which was contained in older versions of this project is +obsolete. Refer to the up-to-date +`specification `_ +in the ``libmodulemd`` project. + Testing -------