Recently, I’ve been scouting around the web for examples of what people have been doing with PEAK-Rules (and the older RuleDispatch package) to get an idea of what else I should put in (if anything) before making the first official release.
One of the interesting things I found was a package called prioritized_methods, which advertises itself as a “prioritized” version of PEAK-Rules, and appears to have been used in the ToscaWidgets project at one point.
Prioritized methods certainly seem like a useful idea, but I was a bit bothered by the specific implementation, because it showed just how weak PEAK-Rules’ extensibility documentation is at this point.
Really, it shouldn’t be that hard to implement manual method priorities in PEAK-Rules. I mean, prioritized_methods is like 150 lines plus docstrings, it has to define several new method types and decorators to replace those in PEAK-Rules, and if you want to use it with a new method type of your own, you’re already screwed by potential incompatibilities.
In short, I clearly wasn’t exposing a good enough API or providing good enough examples. 😉
So, there had to be a better way, and in fact I immediately thought of one that ought to be doable in a dozen lines of code or so, that would make a perfect demo for PEAK-Rules’ predicates module documentation:
from peak.rules import implies, when
from peak.rules.criteria import Test
from peak.rules.predicates import Const, expressionSignature
class priority(int):
"""A simple priority"""
when(implies, (priority, priority))(lambda p1,p2: p1>p2)
@when(expressionSignature,
"isinstance(expr, Const) and "
"isinstance(expr.value, priority)")
def test_for_priority(expr):
return Test(None, expr.value)
What this code does is create an integer subclass called priority
, that can then be used in rule definitions, e.g.:
@when(some_func, "isinstance(foo, Bar) and priority(1)")
Then, between two otherwise-identical rules, the one with a priority will win over the one without, or the higher priority will win if both have priorities.
All you have to do to use it, is import the priority
type into the modules where you want to use it. No new decorators or special method types are needed, and it will continue to work with any new method types added to PEAK-Rules or defined by third parties!
Pretty neat, huh?
There was just one downside to it, and that’s that it didn’t work. 🙁
As it happens, PEAK-Rules’ predicate dispatch engine barfed on using None as a test expression (in the Test(None, expr.value)
part), and I had to tweak a few lines to make it skip over indexing and code generation for tests on None. But, once that was done, I was able to add a tested version of the above as a doctest demo.
Anyway, if you’re doing anything interesting with PEAK-Rules, or find yourself needing to extend it in some way, I’d love to hear from you. Right now, it’s pretty easy for me to add cool features like the one above, but I’m guessing that there are still some gaps in the current documentation for anybody else trying to implement nifty new features like the above.
So, I’m especially interested in any problems you had doing extensions, as well as success stories. (I’d really like to start firming up the extension APIs soon, as well as their docs!)
Hi!
Sorry for not yet replying to your comment about RUM-generic.
Unfortunately I just separated this package from RUM and host it
on bitbucket.
This package was written as well as priorized_methods
by Alberto Valverde and I am not very deep involved with peak.rules.
By the way, I was the intuition driven optimizer:
http://www.eby-sarna.com/pipermail/peak/2009-January/003184.html
I really would like to incorporate your code into priorized_method
when there is a new peak.rules including your changes to the core.
That looks like a pretty awesome improvement and should have great effects
for the mantainibility of our software.
Moreover I hope, that it might also improve our performance.
Thank you very much for such a great software and all your help.
Cheers,
Michael
P.S.: By the way, your "Python is not Java", "Java is not Python" article
belong to the best blog posts I ever read.
The core changes are available in the current nightly snapshot, so you can use them now by requiring "PEAK-Rules>=0.5a1dev-r2662".
I'm actually planning to include an implementation of "priority" sometime soon, though, so it might be moot. 😉
Nice article. An official release with a similar "official" priority implementation would be greatly welcome; there seems to be a need for it.
One small disadvantage of this trick is that "and priority(x)" reads a bit confusing, because it sounds as if the priority belonged to the condition, not to the function. I found the separate priority argument of prioritized_method a bit more readable.
Also, how can I extend this trick to tuples of types instead of strings as conditions?
Hi Christoph. For using this with TurboJson, I recommend one of the approaches described here. Let me know what you think.
Right, I think that's the way how TurboJson should have worked from the beginning. The posting is really helpful and should go into the docs. Maybe you should add that you can add another "around" method on top of your "around" method etc. if one level does not suffice.
You're right, use of priorities can actually represent a code smell.
Anyway I'll try to think up an example where they could be useful 😉
Does this progress mean that PEP 3124 is still a WIP?
Not really. I may come back to that eventually, when I actually use Python 3 for anything.