[PEAK] issues while porting priority_methods to use peak.rules.predicates.priority

P.J. Eby pje at telecommunity.com
Tue Aug 17 18:51:21 EDT 2010

At 11:21 PM 8/17/2010 +0200, Alberto Valverde wrote:
>  Hi,
>Recent changes in PEAK-Rules have broken prioritized_methods.

Anything other than the move of always_overrides?  (which I already reverted)

>In order not to break clients (eg: turbojson and rum) and give them 
>some time to upgrade their rules using the mechanisms provided by 
>peak.rules, I'd like to re-implement it as a thin compatibility 
>layer using the new "priority" criterion in peak.rules.
>However, I've found a slight semantic difference which I'm not sure 
>how to emulate, the following test (adapted from one inside 
>prioritized_methods' test suite) illustrates it:
>class TestPriority(unittest.TestCase):
>     def test_more_specific_sig_overrides(self):
>         from peak.rules.predicates import priority
>         def f(n, m):
>             return n
>         when(f, "n==5")(lambda n, m: n+5) #f1
>         when(f, "n==5 and priority(1)")(lambda n, m: n+10) #f2
>         when(f, "n==5 and m==5")(lambda n, m: n+25) #f3
>         self.failUnlessEqual(f(5,1), 15)
>         self.failUnlessEqual(f(5,5), 25) # raises AmbiguousMethods 
> between f2 and f3
>Is there any way to tell peak.rules to consider that a "priority" is 
>always implied by any other criterion so the fact that it is present 
>in a predicate doesn't raise the predicate's selectiveness? (does 
>that make sense?)

This would do what you're *asking*:

   when(implies, (object, priority))(value(True))

However, I don't think it does what you *want*.

Signature(a) implies Signature(b) if and only if ALL elements in b 
are implied by SOME item in a.   So, let's say that b has a priority 
in it -- that would mean that b's priority is ignored in the ranking, 
since it's implied by anything in a...  and it wouldn't matter 
whether a had any priority at all!

So, that's not really what you *want*, which is to use priority only 
as a tie-breaker -- and then only for determining method override order.

Maybe something like:

@around(overrides, (Method, Method))
def check_priority(next_method, a1, a2):
     if not next_method(a1, a2):
         return (not implies(a2.signature, a1.signature)
                 and priority_of(a1.signature) > priority_of(a2.signature))
     return True

def priority_of(sig):
     for t in sig:
         if isinstance(t, Test) and t.expr is None and 
             return t.value
     return 0

This is kind of messy; the reason the "not implies" part is needed is 
that you don't want to have priority overrule an implication in the 
other order.

This also wouldn't affect the order of before/after type rules, since 
those are combined by implication rather than method override 
order.  One could use the same trick for when(implies, (Signature, 
Signature)) instead of around(overrides, (Method,Method)), but I'm a 
smidge worried about putting it anywhere -- it sort of makes priority 
central to the entire rules system instead of something that's a nifty add-on.

Give it a try either way, though, and see what you think (and whether 
it really works) before I put it in officially.

>P.S Thanks for the recent efforts in documenting peak.rule's internals!

What did you find most useful/enlightening?

More information about the PEAK mailing list