Opened 12 years ago

Closed 11 years ago

Last modified 11 years ago

#18979 closed Bug (fixed)

PermWrapper + template "if in" interaction

Reported by: Anssi Kääriäinen Owned by: Anssi Kääriäinen
Component: Template system Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Trying to do

{% if 'someperm' in perms.someapp %}has perm{% else %}no perm{% endif %}

will result in endless loop. Above, the perms is PermWrapper as installed by the RequestContext.

Doing if perms.someapp.someperm  works correctly. I tried the above because I have a permission codename (from external database) which contains '-', so I can't use the documented syntax.

The attached tester project shows this error. run devserver, click the link, and you will have the dev-server in endless loop which isn't even killable by Ctrl-c... So, be prepared to kill the server by force.

I know the above isn't documented use of PermWrapper. But, to me it seems this bug isn't a PermWrapper bug, what it does looks sane to me. So, I am suspecting there could be some underlying bug in the template engine. So, I am marking this into Template system, though the bug could be elsewhere, too.

Tested with 1.4.1 and 1.5.dev20120918050907 with Python 2.7.3.

Attachments (1)

test_project.tar.gz (8.3 KB ) - added by Anssi Kääriäinen 12 years ago.

Download all attachments as: .zip

Change History (6)

by Anssi Kääriäinen, 12 years ago

Attachment: test_project.tar.gz added

comment:1 by Anssi Kääriäinen, 12 years ago

I am completely lost with this ticket... I replaced the contents of the some_view (testing/views.py) with this:

def some_view(request):
    context_instance=RequestContext(request)
    testing_perms = context_instance['perms']['testing']
    'foo' in testing_perms
    ret =  render_to_response('a_template.html', {'foo': 'bar'},
                              context_instance=RequestContext(request))
    return ret

Then, I placed a print(__getitem__(%s) % perm_name) into django/contrib/auth/context_processors.py:PermLookupDict.__getitem__. . Access the view and it prints this endlessly:

__getitem__(0)
__getitem__(1)
__getitem__(2)
...

The stack trace is this:

^Cakaariai@akaariai-UX31E:~/Programming/tester$ python manage.py runserver
Validating models...

0 errors found
September 18, 2012 - 13:46:04
Django version 1.5.dev20120917231511, using settings 'tester.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
__getitem__(0)
> /home/akaariai/Programming/tester/django/contrib/auth/context_processors.py(14)__getitem__()
-> return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
(Pdb) perm_name
0
(Pdb) bt
  /usr/local/lib/python2.7/threading.py(524)__bootstrap()
-> self.__bootstrap_inner()
  /usr/local/lib/python2.7/threading.py(551)__bootstrap_inner()
-> self.run()
  /usr/local/lib/python2.7/threading.py(504)run()
-> self.__target(*self.__args, **self.__kwargs)
  /usr/local/lib/python2.7/SocketServer.py(582)process_request_thread()
-> self.finish_request(request, client_address)
  /usr/local/lib/python2.7/SocketServer.py(323)finish_request()
-> self.RequestHandlerClass(request, client_address, self)
  /home/akaariai/Programming/tester/django/core/servers/basehttp.py(140)__init__()
-> super(WSGIRequestHandler, self).__init__(*args, **kwargs)
  /usr/local/lib/python2.7/SocketServer.py(638)__init__()
-> self.handle()
  /usr/local/lib/python2.7/wsgiref/simple_server.py(124)handle()
-> handler.run(self.server.get_app())
  /usr/local/lib/python2.7/wsgiref/handlers.py(85)run()
-> self.result = application(self.environ, self.start_response)
  /home/akaariai/Programming/tester/django/contrib/staticfiles/handlers.py(71)__call__()
-> return self.application(environ, start_response)
  /home/akaariai/Programming/tester/django/core/handlers/wsgi.py(236)__call__()
-> response = self.get_response(request)
  /home/akaariai/Programming/tester/django/core/handlers/base.py(115)get_response()
-> response = callback(request, *callback_args, **callback_kwargs)
  /home/akaariai/Programming/tester/testing/views.py(7)some_view()
-> 'foo' in testing_perms
> /home/akaariai/Programming/tester/django/contrib/auth/context_processors.py(14)__getitem__()
-> return self.user.has_perm("%s.%s" % (self.module_name, perm_name))

Tested both on Python 3.2 and Python 2.7, and at least 1.4.0 has this already.

comment:2 by Anssi Kääriäinen, 12 years ago

Owner: changed from nobody to Anssi Kääriäinen
Triage Stage: UnreviewedAccepted

An irc-discussion with Alex, and the reason for this is now clear. The reason is that if there isn't __iter__ defined for an object, but it has a __getitem__ which never raises IndexError, then the 'something' in obj will continuously check for obj[i] == 'something'; i++...

The solution is to define an __iter__ which raises TypeError.

I am assigning this to myself. The only question is how to test this... A possible test would be to check False not in perms - the reason is that __getitem__ will return False for the first index, 0, and then the in check will see False in the perms incorrectly. And, there should be no possibility of forever-loop.

I am going to backpatch this to 1.4, too.

comment:3 by Anssi Kääriäinen, 12 years ago

Triage Stage: AcceptedReady for checkin

comment:4 by Anssi Kääriäinen <akaariai@…>, 11 years ago

Resolution: fixed
Status: newclosed

In 50d573d2c0b3e17cbf1aa240b03b52e4ad0c32cd:

Fixed #18979 -- Avoid endless loop caused by "val in PermLookupDict"

Fixed by defining iter which raises TypeError. This was done to
PermWrapper earlier.

comment:5 by Anssi Kääriäinen <akaariai@…>, 11 years ago

In 1f537335d9ff659cb0996d6523ad8ab7b3c49f4e:

[1.4.x] Fixed #18979 -- Avoid endless loop caused by "val in PermLookupDict"

Fixed by defining iter which raises TypeError. This was done to
PermWrapper earlier.

Backport of 50d573d2c0b3e17cbf1aa240b03b52e4ad0c32cd

Note: See TracTickets for help on using tickets.
Back to Top