From 29bc13d68d05b0cb205d4a4d48ca6fe3386f658f Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Wed, 29 Apr 2015 19:46:27 +0000 Subject: WIP: morph diff --- morphlib/cmdline_parse_utils.py | 89 +++++++++++++++++++++++++++++++++++++++++ morphlib/plugins/diff_plugin.py | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 morphlib/cmdline_parse_utils.py create mode 100644 morphlib/plugins/diff_plugin.py diff --git a/morphlib/cmdline_parse_utils.py b/morphlib/cmdline_parse_utils.py new file mode 100644 index 00000000..c5e08ec1 --- /dev/null +++ b/morphlib/cmdline_parse_utils.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Copyright © 2015 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . + + +from itertools import groupby as _groupby +from itertools import takewhile as _takewhile + +from cliapp import AppException as _AppException + + +from .utils import word_join_list as _word_join_list + + +def _split(iterable, split_token): + sequence = [] + for token in iterable: + if token == split_token: + yield tuple(sequence) + sequence = [] + else: + sequence.append(token) + if sequence: + yield tuple(sequence) + + +class SystemsSpecsParseWrongNumber(_AppException): + def __init__(self, specs, definitions_names): + self.specs = specs + self.definitions_names = definitions_names + msg = 'From expected definition specs {expected};'.format( + expected=_word_join_list(map(repr, definitions_names))) + if len(specs) < len(definitions_names): + missing_spec_names = definitions_names[len(specs):] + super(SystemsSpecsParseWrongNumber, self).__init__( + '{msg} missing {missing}'.format( + msg=msg, + missing=_word_join_list(map(repr, missing_spec_names)))) + else: + super(SystemsSpecsParseWrongNumber, self).__init__( + '{msg} {extra} extra specs given'.format( + msg=msg, + extra=len(specs) - len(definitions_names))) + + +class SystemsSpecsParseWrongFormat(_AppException): + def __init__(self, definition_list_name_list, malformed_definition_lists): + self.definition_list_name_list = definition_list_name_list + self.malformed_definition_lists = malformed_definition_lists + errors = [] + for spec, name, i in malformed_definition_lists: + pre = 'Spec {i} named {name!r}'.format(i=i, name=name) + if not spec: + errors.append(pre + ' is empty, want REPO REF SYSTEM...') + elif len(spec) == 1: + errors.append(pre + ' missing REF, want REPO REF SYSTEM...') + super(SystemsSpecsParseWrongFormat, self).__init__( + 'From expected definition specs {expected}:\n\t{errors}'.format( + expected=_word_join_list(map(repr, definition_list_name_list)), + errors='\n\t'.join(errors))) + + +def parse_definition_lists(definition_list_name_list, args): + specs = tuple(_split(args, '-')) + if len(specs) != len(definition_list_name_list): + raise SystemsSpecsParseWrongNumber(specs, definition_list_name_list) + + malformed_definition_lists = [] + specinfo = enumerate(zip(definition_list_name_list, specs), start=1) + for i, (definition_list_name, definitions_spec) in specinfo: + if len(definitions_spec) < 2: + malformed_definition_lists.append( + (definitions_spec, definition_list_name, i)) + if malformed_definition_lists: + raise SystemsSpecsParseWrongFormat(definition_list_name_list, + malformed_definition_lists) + + return ((spec[0], spec[1], spec[2:]) for spec in specs) diff --git a/morphlib/plugins/diff_plugin.py b/morphlib/plugins/diff_plugin.py new file mode 100644 index 00000000..4763ba40 --- /dev/null +++ b/morphlib/plugins/diff_plugin.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Copyright © 2015 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . + + +import cliapp + +from morphlib.cmdline_parse_utils import parse_cmdline_system_lists +from morphlib.morphologyfinder import MorphologyFinder +from morphlib.morphloader import MorphologyLoader +from morphlib.util import new_repo_caches + + +class DiffPlugin(cliapp.Plugin): + + def enable(self): + self.app.add_subcommand( + 'definition-diff', self.definition_diff, + arg_synopsis='REPO REF [SYSTEM]... - REPO REF [SYSTEM]...') + + def disable(self): + pass + + def definition_diff(self, args): + '''Show the difference between definitions. + + When given two definition file specifiers, prints the logical + differences between the definitions. + + ''' + # NOTE: It would be more useful to have this operate at the graphed + # dependency level, so you could use it to compare two different + # systems, and avoid duplicated logic to interpret how + # definitions are parsed. + # However at the time of writing the data model does not support: + # 1. Separately loading definition files from (repo, ref) and + # having the loaded definitions being shared between computed + # sources. + # 2. Parsing definitions for multiple systems together. + # This is because parameters from the parent definition (i.e. + # arch) are passed down to included definitions, but this is + # not taken into account for the lookup table, which is keyed + # on name, so it can't handle two chunks with the same name + # but different architectures. + from_spec, to_spec = parse_definition_lists( + definition_list_name_list=('from', 'to'), + args=args) + + lrc, rrc = new_repo_caches(self.app) + ml = MorphologyLoader() + + + def load_morphset((reponame, ref, definitions)): + repo = lrc.get_updated_repo(reponame, ref=ref) + mf = MorphologyFinder(gitdir=repo.gitdir, ref=ref) + if not definitions: + definitions = mf.list_morphologies() + ms = MorphologySet() + for definition in definitions: + m = ml.parse_morphology_text(mf.read_morphology(definition), + definition) + ms.add_morphology(m) + from_morphset = load_morphset(from_spec) + to_morphset = load_morphset(to_spec) -- cgit v1.2.1