[Grok-dev] view predicates in Grok?

Chris McDonough chrism at plope.com
Mon Jan 4 15:32:44 EST 2010

Martijn Faassen wrote:
> Hey Paul,
> This is extremely helpful, thank you very much (and to Chris for helping 
> him). It becomes easier to talk about it.
> Do I understand correctly that a predicate function's return value is 
> what is being checked here? I.e. in this case:
>     grok.predicate(foo="some value")
> a predicate 'foo' is looked up and called with context and request, and 
> if it returns "some value" then the predicate applies.

No, a predicate callable is called with context and request, and returns True 
or False.

Here's an example of something that might be called at configuration time

def make_predicates(request_method=None):
     predicates = []
     if request_method is not None:
         def request_method_predicate(context, request):
             return request.method == request_method
     return predicates

... where "make_predicates" is called as the result of processing a ZCML 
directive or martian directive related to a single view.  A more real-world 
collection of predicates set up similarly is in the "_make_predicates" function 
inside http://svn.repoze.org/repoze.bfg/trunk/repoze/bfg/configuration.py

Hopefully, knowing this, something like this starts to make sense:

class Foo(grok.View):

> Paul Wilson wrote:
> [snip]
>> It's also worth noting that the BFG system allows you to define a list of
>> predicate functions that are also involved in the view selection process.
>> For example, you can define a function:
>> def predicate1(request, context):
>> 	if request.header['foo'] == 'bar' and
>> 	   context.foo == 'bar':
>> 		return True
>> and specify it as follows:
>> class FooView(grok.View):
>> 	grok.context(...)
>> 	grok.name(...)
>> 	grok.predicate(containment=MyContainer,
>> 		       custom_predicates=[predicate1,
>> 		                          predicate2,
>> 					  predicate3])
>> 	...
>> This allows any kind of logical spelling to be given as part of the view
>> matching process, inside or outside of the domain of requests and contexts.
>> Want your views to be selected based on prevailing weather conditions? No
>> problem!
>> I did entertain the idea of allowing the provided predicates to be used within
>> your predicate function:
>> class FooView(grok.View)
>> 	grok.context(...)
>> 	grok.name(...)
>> 	grok.predicate.containment(MyContainer)
>> 	grok.predicate.custom_predicates=predicate1 # Can also be a sequence
>> 	...
>> def predicate1(request, context):
>> 	if grok.predicate.request_method('POST') and
>> 	   context.foo == 'bar':
>> 		return True
>> Using some deep Python magic this is probably possible, but it is
>> unnecessary magic;
>> the request API is simple enough anyway for you to be able to feasibly
>> recreate any of
>> the predicate's work anyway! However, splitting the predicates out
>> like this would allow us
>> to use martian's built in checkers to perform validation on predicates
>> in a natural way. Also,
>> we could inherit predicates from parent views too:
>> class POSTView(grok.View):
>> 	grok.predicate.request_method('POST')
>> Any thoughts?
> Are the "standard" BFG predicates registered globally?

They are not "registered", per se.  A set of "convenience" predicates exists in 
the _make_predicates function I mentioned previously within 
http://svn.repoze.org/repoze.bfg/trunk/repoze/bfg/configuration.py . 
Predicates that we didn't anticipate are addable via a "custom_predicates" 
argument to view configuration.

> Would it make 
> sense to register custom predicates through Martian, perhaps with a 
> decorator like this?
> @grok.view_predicate('predicate1')
> def predicate1(request, context):
>     ...

That's up to you.  My personal opinion: no.  It'd be too throw-and-catch; the 
framework needn't provide any predicate "registrations" unless the 
"custom_predicates" mechanism of providing.. well, custom predicates.. doesn't 
work for some reason.

> This might also make it easier for predicates to reuse each other, as 
> the decorator would just help to register the predicate itself and not 
> alter the function's behavior otherwise. Validation on grok.predicate 
> could then also be done pretty easily.
> Or is it not good to be global here and better to register this stuff 
> locally? The main issue I can see is naming clashes as there is only a 
> single namespace available, but that may be acceptable.

I personally wouldn't treat adding new convenience predicates as any sort of 
registration problem.  Instead, I'd just have the martian directive accept 
keyword arguments that relate to "built-in" predicates and inline those 
predicate definitions into a closure within the configuration logic.  When you 
want to add a new convenience predicate, change that closure.  Cases not 
covered by convenience predicates can be handled by the user passing 
"custom_predicates=(pred1, pred2)" to the directive.

- C

More information about the Grok-dev mailing list