Skip to content
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

Fixed #24977 -- Made template tags treat undefined variables not equal to None. #7901

Closed
wants to merge 1 commit into from
Closed

Conversation

timmartin
Copy link
Contributor

The template engine no longer treats undefined variables as being None,
but instead as an instance of a special UndefinedVariable class.

self.string_rep = string_rep

def __nonzero__(self):
return False
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use __bool__ as master is Python 3 only.

return self.string_rep

def __str__(self):
return unicode(self).encode('utf-8')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make __str__ return self.string_rep and nuke __unicode__.

obj = string_if_invalid
obj = UndefinedVariable(string_if_invalid)
else:
obj = UndefinedVariable(string_if_invalid)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to pass an explicit empty string here or make UndefinedVariable.string_rep default to it and don't pass any arg along.

return isinstance(other, UndefinedVariable)

def __ne__(self, other):
return not isinstance(other, UndefinedVariable)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not required as __ne__ defaults to not self.__eq__(other) on Python 3.

@@ -682,22 +714,19 @@ def __init__(self, token, parser):
self.filters = filters
self.var = var_obj

def resolve(self, context, ignore_failures=False):
def resolve(self, context):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess ignore_failures should go through a deprecation path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was assuming this wasn't necessary since FilterExpression isn't part of the public API, and all the internal usages have been updated.

Copy link
Member

@timgraham timgraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some discussion in the "backwards incompatible changes" section of docs/releases/2.0.txt summarizing these changes would be useful. While these changes might be considered bug fixes, I could imagine they might affect a few projects that are inadvertently broken.

else:
with self.assertRaises(TemplateSyntaxError):
self.engine.render_to_string('exception02')
with self.assertRaises(TemplateSyntaxError) as exception_ctx:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use assertRaisesMessage here rather than checking the message separately.

@setup({'if-tag-invalid-member': '{% if x.non_existent == None %}yes{% else %}no{% endif %}'})
def test_if_tag_invalid_member(self):
"""
Invalid members should not be regarded as equal to None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

members -> attributes

@@ -166,7 +166,7 @@ def test_url_fail09(self):

@setup({'url-fail11': '{% url named_url %}'})
def test_url_fail11(self):
with self.assertRaises(NoReverseMatch):
with self.assertRaises(TemplateSyntaxError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use assertRaisesMessage

@@ -1501,6 +1501,11 @@ For example::

If ``value`` is ``None``, the output will be the string ``"nothing"``.

.. versionchanged:: 2.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the behavior of undefined variables isn't documented, I don't see a need for this note unless you also add that documentation. The note should be self-contained as described in https://docs.djangoproject.com/en/dev/internals/contributing/writing-documentation/#documenting-new-features.


def __init__(self, string_rep=""):
"""
:param string_rep: The string value that this expression should
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use JavaDoc style.

`string_repo` is the ...

would be fine. Maybe call it var_repr (usually variables aren't named after the type that they are).

@timgraham timgraham changed the title Fixed #24977 -- Templates treat undefined as distinct from None Fixed #24977 -- Made template tags treat undefined variables not equal to None. Feb 7, 2017
The template engine no longer treats undefined variables as being None,
but instead as an instance of a special UndefinedVariable class.
Copy link
Member

@timgraham timgraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left an idea on the mailing list thread about possible security implications that this change could have.

It could be useful to break this up into separate commits, one for each tag that's changed. It should make it easier to review and some changes like {% url %} are more straightforward and less risky than {% if %}.

else:
with self.assertRaises(TemplateSyntaxError):
self.engine.render_to_string('exception02')
with self.assertRaisesRegexp(TemplateSyntaxError,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use:

msg = "Got this from the 'nonexistent' variable"
with self.assertRaisesMessage(TemplateSyntaxError, msg):

`assertRraisesMessage

context. All instances are treated as being equal to each other.
"""

def __init__(self, var_rep=""):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think var_repr is more explanatory than just rep. Also, use single quotes rather than double quotes unless the string contains a single quote.

@@ -599,6 +601,14 @@ def test_if_is_not_variable_missing(self):
@setup({'template': '{% if foo is not bar %}yes{% else %}no{% endif %}'})
def test_if_is_not_both_variables_missing(self):
output = self.engine.render_to_string('template', {})
self.assertEqual(output, 'yes')

@setup({'if-tag-invalid-member': '{% if x.non_existent == None %}yes{% else %}no{% endif %}'})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

member -> attribute

@@ -271,6 +271,25 @@ If you wish to keep this restriction in the admin when editing users, set
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)

Undefined variables in templates are distinguished from `None`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

double backticks must be used for correct formatting of the rendered docs

Undefined variables in templates are distinguished from `None`
--------------------------------------------------------------

In templates, undefined values and non-existent attributes of objects
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nonexistent (no dash)

@timgraham
Copy link
Member

Closing per ticket (wontfix).

@timgraham timgraham closed this Apr 27, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants