On Monday 30 November 2009 11:18:13 Russell Keith-Magee wrote:
> > 1) Handling non-existent variables
> > ==================================
...
> > To do this properly, I think the best approach would be something
> > like "almost three valued logic", which would work as follows:
> >
> > 1) variables that don't exist are represented by Null
> > 2) all operators return either True or False, never Null
> > 3) for 'not', 'and' and 'or', Null effectively acts like False
> > (so "not Null" == True, "Null or True" == True etc)
> > 4) for all comparison operators, if either argument is Null then
> > the result is False e.g. "Null == Null" is False
> > "Null > 1" is False, "Null <= 1" is False, except
> > "!=", which should return true if either value is Null.
> >
> > Any comments on this approach? I've started to implement this,
> > and it works OK so far, just needs a bit more work.
>
> I was with you right up until the equality comparisons (Null ==
> Null -> False etc). As noted by Alex, it conflicts with the answer
> given by {% ifequal %}; it also differs with Python's behavior.
Yeah, I hadn't thought of that. I don't think it's a case of differing
with Python's behaviour, as we are making a deliberate choice to
substitute a missing variable with None (or Null or whatever). You
could argue that Python throws a NameError in this case.
I was trying to think about what is intuitive. e.g. what should happen
here if foo has not been supplied:
{% if foo < 1 %}
yes
{% endif %}
I think it is fairly counter-intuitive for a template author that you
get 'yes' here. However, on further reflection, the "almost 3 valued
logic" can be equally counter-intuitive, say with the following case:
{% if foo < 1 %}
yes
{% else %}
no
{% endif %}
It would render 'no' if foo was missing, but it might be quite
surprising that if you decided to re-arrange the template by inverting
the logical condition:
{% if foo >= 1 %}
no
{% else %}
yes
{% endif %}
then you get different behaviour. (i.e. 'yes' if foo was missing).
So, I'm *now* suggesting that we convert everything missing to None.
In fact, I've found that doing anything apart from this would be hard.
(Alex, you were right about what Malcolm had done with
FilterExpression, which resolves the problem with
TEMPLATE_STRING_IF_INVALID, but this gives me None instead of '', and
not information about whether the variable actually exists, which is a
slightly different question. I didn't find this out earlier because
most of the tests are against the 'IfParser' which is a subclass of
'TemplateIfParser', and works slightly differently, and because I
wasn't thinking properly).
Behaviour of 'not' - revisited
==============================
I think this leaves just the behaviour of 'not'. It is a bit more
complicated than Alex suggested — the tests include "if not" and "if
not not", and with the current behaviour you can do things like "if
foo and not not" etc.
In fact, you can do it (to some extent) with *any* of the keywords
e.g. "if and", "if x and or". But not always e.g. "if not or and x"
- that, illogically, is a syntax error.
I suspect that allowing everything that was possible before will be
extremely difficult, because the current behaviour is very badly
defined for these edge cases — it works just fine if you do sensible
things like not using variables called 'not', 'and' or 'or', but
working out the rules for the exceptional cases will be very hard.
I propose a backwards incompatibility here, it looks like this:
The 'if' tag no longer accepts 'and', 'or' and 'not' as valid
variable names. Previously that was allowed in some cases even
though they were normally treated as keywords. Now, the keyword
status is always enforced, and code like {% if not %} or {% if and %}
will throw a TemplateSyntaxError.
This would now be the only backwards incompatibility (discounting the
possibility that people were relying on TemplateSyntaxError for things
that are now legal).
Patch
=====
It can be tracked in my 'lp-smartif' branch here:
http://bitbucket.org/spookylukey/django-trunk-lukeplant/
Currently various tests about 'not' are failing, and docs are not
written.
Chris: in the course of my attempted 'Null' behaviour changes, I
changed your implementation slightly — I added explicit classes for
Less, LessOrEqual, NotEqual and Not, pulling out the 'negate'
behaviour from the individual classes (it made implementing the Null
logic simpler). Other than that, very little of the parsing has
changed. Given that the 'Null' stuff has now been removed, we could
move back to your way to reduce the code a bit, but I'm not sure it is
worth it.
Review would be welcome, especially as I'm ill at the moment. I'm only
coding because the boredom of doing nothing is killing me...
Luke
--
"Humiliation: The harder you try, the dumber you look."
(
despair.com)
Luke Plant ||
http://lukeplant.me.uk/