summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2015-04-29 19:46:27 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2015-05-06 14:00:43 +0000
commit29bc13d68d05b0cb205d4a4d48ca6fe3386f658f (patch)
tree7adfd68e8a9e31245c5cde18e873ec1eedad6b96
parent49c75289090767256b06a20ded55ad58bd9932d6 (diff)
downloadmorph-29bc13d68d05b0cb205d4a4d48ca6fe3386f658f.tar.gz
WIP: morph diff
-rw-r--r--morphlib/cmdline_parse_utils.py89
-rw-r--r--morphlib/plugins/diff_plugin.py75
2 files changed, 164 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+
+
+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 <http://www.gnu.org/licenses/>.
+
+
+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)