From 927249ea2ea59457b36dae7ee60b61b6dfe10821 Mon Sep 17 00:00:00 2001 From: David Rajaratnam Date: Wed, 27 Oct 2021 09:43:22 +1100 Subject: [PATCH] Added a `sorted` flag to FactBase.asp_str() for sorted output --- clorm/orm/factbase.py | 45 +++++++++++++++++++++++++++++++------ tests/test_orm_factbase.py | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/clorm/orm/factbase.py b/clorm/orm/factbase.py index 88cded1..2ce18b5 100644 --- a/clorm/orm/factbase.py +++ b/clorm/orm/factbase.py @@ -41,6 +41,8 @@ # PEP 257 (modified for Python 3): https://www.python.org/dev/peps/pep-0257/ # ------------------------------------------------------------------------------ +_builtin_sorted=sorted + def _format_asp_facts(iterator,output,width): tmp1="" for f in iterator: @@ -360,22 +362,51 @@ def facts(self): tmp = [ fm.factset for fm in self._factmaps.values() if fm] return list(itertools.chain(*tmp)) - def asp_str(self,width=0,commented=False): - """Return a string representation of the fact base that is suitable for adding - to an ASP program + def asp_str(self,*,width=0,commented=False,sorted=False): + """Return a ASP string representation of the fact base. + + This ASP string representation is syntactically correct ASP code so is + suitable for adding as the input to to an ASP program (or writing to a + file for later use in an ASP program). + + By default the order of the facts in the string is arbitrary. Because + `FactBase` is built on a `OrderedDict` (which preserves insertion + order) the order of the facts will be deterministic between runs of the + same program. However two FactBases containing the same facts but + constructed in different ways will not produce the same output + string. In order to guarantee the same output the `sorted` flag can be + specified. + + Args (keyword arguments only): + + width=0: tries to fill to a given width by putting more than one + fact on a line if necessary. + + commented=False: produces commented ASP code by adding a predicate + signature and turning the Predicate sub-class + docstring into a ASP comments. + + sorted=False: sort the output facts """ self._check_init() # Check for delayed init out = io.StringIO() - if not commented: - _format_asp_facts(self,out,width) + first=True + if sorted: + names = _builtin_sorted(self._factmaps.keys(),key=lambda pt: + (pt.meta.name, pt.meta.arity,pt.__name__)) + fms = [self._factmaps[n] for n in names] else: - first=True - for fm in self._factmaps.values(): + fms = self._factmaps.values() + for fm in fms: + if commented: if first: first=False else: print("",file=out) _format_commented(fm,out) + if sorted: + _format_asp_facts(_builtin_sorted(fm.factset),out,width) + else: _format_asp_facts(fm.factset,out,width) data = out.getvalue() diff --git a/tests/test_orm_factbase.py b/tests/test_orm_factbase.py index e7044e6..861423b 100644 --- a/tests/test_orm_factbase.py +++ b/tests/test_orm_factbase.py @@ -553,6 +553,52 @@ class Meta: name="a_very_long_predicate_name_that_cause_wrapping_well" cfactspre="% Predicate signature:\n% {}(n,s)\n".format(C.meta.name) self.assertTrue(aspstr.startswith(cfactspre)) + #-------------------------------------------------------------------------- + # Test the asp output string with the sorted flag + # -------------------------------------------------------------------------- + def test_factbase_aspstr_sorted(self): + class A(Predicate): + a = IntegerField + class Meta: name="bb" + class B(Predicate): + a = IntegerField + class Meta: name="aa" + class C(Predicate): + a = IntegerField + b = IntegerField + class Meta: name="aa" + + def tostr(facts): + return ".\n".join([str(f) for f in facts]) + + def sig(ptype): + cstr = ("% -------------------\n" + "% Predicate signature\n" + "% {}(a)\n" + "% -------------------\n").format(ptype.__name__) + return cstr + + afacts = [A(100),A(50),A(99)] + bfacts = [B(100),B(50),B(99)] + cfacts = [C(100,100),C(50,50),C(99,99)] + fb=FactBase() + fb.add(cfacts) + fb.add(bfacts) + fb.add(afacts) + + expected1 = tostr(cfacts) + ".\n" + tostr(bfacts) + ".\n" + \ + tostr(afacts) + ".\n" + self.assertEqual(fb.asp_str(), expected1) + + expected2 = tostr(sorted(bfacts)) + ".\n" + tostr(sorted(cfacts)) + \ + ".\n" + tostr(sorted(afacts)) + ".\n" + self.assertEqual(fb.asp_str(sorted=True), expected2) + + expected3 = \ + sig(A) + tostr(sorted(bfacts)) + ".\n" + \ + sig(B) + tostr(sorted(bfacts)) + ".\n" + \ + sig(C) + tostr(sorted(afacts)) + ".\n" + self.assertTrue(fb.asp_str(commented=True,sorted=True), expected3) #------------------------------------------------------------------------------ # Test QueryAPI version 1 (called via FactBase.select() and FactBase.delete())