diff --git a/matter/matter/binpms/entropysrv.py b/matter/matter/binpms/entropysrv.py index 3ec571df9..02eceeaac 100644 --- a/matter/matter/binpms/entropysrv.py +++ b/matter/matter/binpms/entropysrv.py @@ -18,6 +18,7 @@ import sys from matter.binpms.base import BaseBinaryResourceLock, \ BaseBinaryPMS +from matter.spec import MatterSpec, MatterSpecParser, GenericSpecFunctions from matter.output import print_info, print_warning, print_error from matter.utils import print_traceback @@ -363,3 +364,29 @@ class EntropyBinaryPMS(BaseBinaryPMS): BaseBinaryPMS.register(EntropyBinaryPMS) BaseBinaryPMS.DEFAULT = False + + +class EntropySpecParser(MatterSpecParser): + """ + External .spec parser object which implements + extra .spec parameters support. + """ + + def __init__(self): + super(EntropySpecParser, self).__init__() + self._funcs = GenericSpecFunctions() + + def vital_parameters(self): + """ + Overridden from MatterSpecParser. + """ + return [] + + def data(self): + """ + Overridden from MatterSpecParser. + """ + return {} + + +MatterSpec.register_parser(EntropySpecParser()) diff --git a/matter/matter/main.py b/matter/matter/main.py index 2da28bfca..5d8660f73 100644 --- a/matter/matter/main.py +++ b/matter/matter/main.py @@ -229,7 +229,7 @@ def main(): avail_binpms = BaseBinaryPMS.available_pms matter_spec = MatterSpec() - parser_data = matter_spec.parser_data_path() + parser_data = matter_spec.data() matter_spec_params = "" for spec_key in sorted(parser_data.keys()): par = parser_data[spec_key] diff --git a/matter/matter/spec.py b/matter/matter/spec.py index 1d2fb3c9b..4c85ca2fc 100644 --- a/matter/matter/spec.py +++ b/matter/matter/spec.py @@ -108,18 +108,19 @@ class GenericSpecFunctions(object): self.valid_path_string(y) and y.strip()] -class MatterSpec(GenericSpecFunctions): +class MatterSpecParser(object): + """ + Base class for implementing external .spec parsers + that can be used to extend the list of supported statements. + """ def vital_parameters(self): """ - Return a list of vital .spec file parameters - - @return: list of vital .spec file parameters - @rtype: list + Return a list of vital parameters. """ - return ["packages", "repository"] + raise NotImplementedError() - def parser_data_path(self): + def data(self): """ Return a dictionary containing parameter names as key and dict containing keys 've' and 'cb' which values are three @@ -129,119 +130,164 @@ class MatterSpec(GenericSpecFunctions): @return: data path dictionary (see ChrootSpec code for more info) @rtype: dict """ - return { + raise NotImplementedError() + + +class MatterSpec(MatterSpecParser): + + _external_parsers = [] + + def __init__(self): + """ + MatterSpec constructor. + """ + self._funcs = GenericSpecFunctions() + + @classmethod + def register_parser(cls, parser): + """ + Register an external parser object. + + @param parser: a MatterSpecParser subclass + @type parser: MatterSpecParser + """ + MatterSpec._external_parsers.append(parser) + + def vital_parameters(self): + """ + Return a list of vital .spec file parameters + + @return: list of vital .spec file parameters + @rtype: list + """ + vital = set(["packages", "repository"]) + for parser in MatterSpec._external_parsers: + vital |= set(parser.vital_parameters()) + return sorted(vital) + + def data(self): + """ + Return a dictionary containing parameter names as key and + dict containing keys 've' and 'cb' which values are three + callable functions that respectively do value extraction (ve), + value verification (cb) and value modding (mod). + + @return: data path dictionary (see ChrootSpec code for more info) + @rtype: dict + """ + data = { "build-args": { - "cb": self.ne_string, - "ve": self.ve_string_shlex_splitter, + "cb": self._funcs.ne_string, + "ve": self._funcs.ve_string_shlex_splitter, "default": ["--verbose", "--nospinner"], "desc": "Portage build arguments (default is --verbose\n " "--nospinner)", }, "build-only": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Only build the packages without merging them\n " "into the system.", }, "dependencies": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow dependencies to be pulled in? (yes/no)", }, "downgrade": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow package downgrades? (yes/no)", }, "keep-going": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Make possible to continue if one \n\t" "or more packages fail to build? (yes/no)", }, "new-useflags": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow new USE flags? (yes/no)", }, "removed-useflags": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow removed USE flags? (yes/no)", }, "rebuild": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow package rebuilds? (yes/no)", }, "spm-repository-change": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow Source Package Manager (Portage) \n\t" "repository change? (yes/no)", }, "spm-repository-change-if-upstreamed": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "In case of Source Package Manager \n\trepository " "changes, allow execution if the original repository " "\n\tdoes not contain the package anymore? (yes/no)", }, "not-installed": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "no", "desc": "Allow compiling packages even if they " "are not \n\tactually installed on the System? (yes/no)", }, "soft-blocker": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "yes", "desc": "Allow soft-blockers in the merge queue?\n " "Packages will be unmerged if yes. (yes/no)", }, "unmerge": { - "cb": self.valid_yes_no, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no, + "ve": self._funcs.ve_string_stripper, "default": "yes", "desc": "Allow package unmerges due to Portage\n " "soft-blockers resolution. (yes/no)", }, "pkgpre": { - "cb": self.not_none, - "ve": self.ve_string_open_file_read, + "cb": self._funcs.not_none, + "ve": self._funcs.ve_string_open_file_read, "default": None, "desc": "Package pre execution script hook path, " "executed \n\tfor each package (also see example files)", }, "pkgpost": { - "cb": self.not_none, - "ve": self.ve_string_open_file_read, + "cb": self._funcs.not_none, + "ve": self._funcs.ve_string_open_file_read, "default": None, "desc": "Package build post execution script hook path, " "executed \n\tfor each package (also see example files)", }, "buildfail": { - "cb": self.not_none, - "ve": self.ve_string_open_file_read, + "cb": self._funcs.not_none, + "ve": self._funcs.ve_string_open_file_read, "default": None, "desc": "Package build failure execution script hook " "path, \n\texecuted for each failing package (also see " "example files)", }, "packages": { - "cb": self.always_valid, - "ve": self.valid_comma_sep_list_list, + "cb": self._funcs.always_valid, + "ve": self._funcs.valid_comma_sep_list_list, "mod": lambda l_l: [x for x in l_l if x], "desc": "List of packages to scrape, separated by " "comma. \n\tIf you want to let Portage consider a group " @@ -249,19 +295,27 @@ class MatterSpec(GenericSpecFunctions): "no commas", }, "repository": { - "cb": self.ne_string, - "ve": self.ve_string_stripper, + "cb": self._funcs.ne_string, + "ve": self._funcs.ve_string_stripper, "desc": "Binary Package Manager repository in where " "newly built \n\tpackages will be put and pushed to", }, "stable": { - "cb": self.valid_yes_no_inherit, - "ve": self.ve_string_stripper, + "cb": self._funcs.valid_yes_no_inherit, + "ve": self._funcs.ve_string_stripper, "default": "inherit", "desc": "Only accept Portage stable packages (no " "unstable keywords)", }, } + for parser in MatterSpec._external_parsers: + parser_data = parser.data() + common_keys = set(data.keys()) & set(parser_data.keys()) + if common_keys: + raise ValueError("Parser %s duplicates support for %s" % ( + parser, " ".join(sorted(common_keys)),)) + data.update(parser_data) + return data class SpecPreprocessor: @@ -372,9 +426,9 @@ class SpecParser: self.file_object = file_object self._preprocessor = SpecPreprocessor(self.file_object) - self.__plugin = MatterSpec() - self.vital_parameters = self.__plugin.vital_parameters() - self._parser_data_path = self.__plugin.parser_data_path() + self.__parser = MatterSpec() + self.vital_parameters = self.__parser.vital_parameters() + self._data = self.__parser.data() def _parse_line_statement(self, line_stmt): try: @@ -400,7 +454,7 @@ class SpecParser: key = None value = None v_key, v_value = self._parse_line_statement(line) - check_dict = self._parser_data_path.get(v_key) + check_dict = self._data.get(v_key) if check_dict is not None: key, value = v_key, v_value old_key = key @@ -410,7 +464,7 @@ class SpecParser: if not value: continue # gather again... key is changed - check_dict = self._parser_data_path.get(key) + check_dict = self._data.get(key) if not isinstance(check_dict, dict): continue value = check_dict["ve"](value) @@ -450,7 +504,7 @@ class SpecParser: Extend parsed data with default values for statements with default option available. """ - for statement, opts in self._parser_data_path.items(): + for statement, opts in self._data.items(): if "default" in opts and (statement not in mydata): mydata[statement] = opts["default"] @@ -459,7 +513,7 @@ class SpecParser: For parser data exposing a mod, execute the mod against the data itself. """ - for statement, opts in self._parser_data_path.items(): + for statement, opts in self._data.items(): if statement in mydata and "mod" in opts: mydata[statement] = opts["mod"](mydata[statement])