-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Predicate call context (WIP) #15
Conversation
@ticosax, want to have a look? |
|
||
def apply(self, *args, **kwargs): | ||
if self.takes_context: | ||
kwargs['context'] = call_context.value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if context is already in kwargs ?
I think we should raise something.
Just an idea: Maybe we can accept an argument that will define de name for context. then the predicate will find it without being forced to use context
if already taken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could make takes_context
to accept either a boolean or a string for the key under which it wants it to be available.
There is also the option of using mypred.context
, the property defined just above (line 67) -- this way there would be no need for **kwargs and the 'context' key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mypred.context
provide a better separation of concerns. I like it better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it better too. It's clearer too -- no need to change the predicate's signature to accept new arguments. Need to add tests to make sure it actually works this way. Will do later today.
aeb78b9
to
01b9ecf
Compare
except IndexError: | ||
return None | ||
|
||
def apply(self, *args, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apply()
turned out to be really ugly. I think I'm going to ditch opening up predicates to accept arbitrary args.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a trade-off which make sense in the context of a django application.
If you consider using django-rules
outside django, then it might become too restricted.
As I'm writing, I do not have such use-case. So it worth the try and reach simplicity.
Otherwise, good work !
5a2bda2
to
61c835d
Compare
I have rewritten the PR. Context is a proper dict subclass providing a single instance attribute:
I'll leave the PR open for a few days, so that it can be reviewed by anyone interested and then perhaps add a couple more tests and merge it into master. I won't write any documentation for it just now, mainly because it's hard for me, but I'd also like the new API to stay "semi-private" for the time being in case I need to change it further. Edit: Forgot to say that a predicate accepting *args will now get passed all arguments, not zero as was the case until now. This is backwards incompatible, but I doubt this mis-feature is used by anyone. |
_context = localcontext() | ||
|
||
|
||
NO_VALUE = frozenset() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can not use an immutable here. Because a legit passed argument might being interpreted as not given.
>>> frozenset() is frozenset()
True
>>> set() is set()
False
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is totally out of context, but reminded me of this article:
http://radar.oreilly.com/2014/10/python-tuples-immutable-but-potentially-changing.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! Not testing this, did me no good :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great article @luzfcb.
I tried to play smart and got bitten. I'll create a simple object subclass with a single nonzero method returning False so that we always get both immutability and identity.
I will test your branch on my project. |
Just pushed a commit that fixes the NO_VALUE issue. There is one issue currently: defining a predicate with arguments that have default values will result in them becoming None due to line 152. It's too late here to fix this just now, so I'll have a look at it tomorrow. |
@ticosax did you get to use this patch? Any feedback? |
@dfunckt sorry, I'm always postponing. But I'll force myself to give a feedback this week. |
No worries. I’ll rebase the branch later today. |
https://github.com/ticosax/django-rules/compare/unstable/call-context I tried it, and I feel the need to improve it a bit to serve for my use-case by adding 2 features:
Note that the implementation is a bit tricky because of of the |
Hi Nicolas, this looks really good to me. I really like both your suggestions re: binding and skipping a predicate. Combined with the ability to store things in context, I think this is a big improvement to rules. I'll merge your changes to this PR and finally merge onto master the soonest possible. |
That's awesome news ! |
Based on information extracted from the context, a predicate might decide than he doesn't have enough data to perform a validation.
60a70ba
to
a23c839
Compare
Just merged this PR. Thanks @ticosax -- awesome work! |
Thank you @dfunckt ! |
Related to #6, #7, #9.
Adds the ability to specify that a predicate wants a dict containing information about the current
test_rule()
invocation.Predicates that want context must accept
**kwargs
.A nice advantage of this PR is that it allows predicates to store arbitrary data that other predicates may later use. It is possible for example, to store an expensive computation done in one predicate (maybe fetch a bunch of objects from db, related to the given user/object) and another predicate access it later on.
This is backwards-compatible -- at least all tests pass.
Here are some things that need to be done in order for this PR to be merged:
All feedback welcome.