[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
predicates.append(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):
grok.predicates(request_method='GET')
...
>
> 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