rgalanakis/hostthedocs

Cannot run under Python 3.5.2 - "TypeError: 'generator' object is not subscriptable"

Closed this issue · 6 comments

TAGC commented

When running the server under Python 2.7.12, everything seems to work. When running it under 3.5.2 and trying to push documentation using the exact same commands, the server returns HTTP 500 (internal server error), and it prints this on console:

  File "C:\Users\davidfallah\AppData\Local\Programs\Python\Python35-32\lib\site-packages\flask\app.py", line 1988, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\davidfallah\AppData\Local\Programs\Python\Python35-32\lib\site-packages\flask\app.py", line 1641, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\davidfallah\AppData\Local\Programs\Python\Python35-32\lib\site-packages\flask\app.py", line 1544, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\davidfallah\AppData\Local\Programs\Python\Python35-32\lib\site-packages\flask\_compat.py", line 33, in reraise
    raise value
  File "C:\Users\davidfallah\AppData\Local\Programs\Python\Python35-32\lib\site-packages\flask\app.py", line 1639, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\davidfallah\AppData\Local\Programs\Python\Python35-32\lib\site-packages\flask\app.py", line 1625, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\davidfallah\Documents\hostthedocs\hostthedocs\__init__.py", line 22, in hmfd
    request.files.values()[0].stream,
TypeError: 'generator' object is not subscriptable

I changed request.files.values()[0].stream to list(request.files.values())[0].stream and this seems to make it work under both Python 2.7.12 and 3.5.2. I have not tested for other versions.

Hmmm, interesting, I have it running under other 3.x versions in Tox/Travis with no problems. Happy to accept that change as a Pull Request, just open it and I'll merge it. Also please add whatever Python 3.5 to the travis/tox configs for extra points!

TAGC commented

Which version of pypy do you test against?

Whatever Travis has as pypy, so I assume the 2.x-compatible line? Check out the .travis.yml and tox.ini. PyPy3 was still pretty young when I set things up.

TAGC commented

Have you actually tried running your server on any version of Python 3? Because I've got the same error when I try running on Python 3.3. I think the issue is just that the tests you've currently got don't expose this bug, so I'll need to add it.

I've also been digging and I think I've found the root cause of the problem. I haven't worked with flask/werkzeug/etc. and I only have a rudimentary understanding of what they are, but the 'request' object you use in request.files.values()[0].stream (where the exception is thrown) is an instance of werkzeug.wrappers.BaseRequest(or close enough). That class exposes a property files which is an instance of werkzeug.datastructures.ImmutableMultiDict. This essentially implements the dict interface, but it's decorated by a function that changes the way it returns an iterator depending on whether you're using a Python 2 or Python 3 interpreter:

def native_itermethods(names):
    if not PY2:
        return lambda x: x

    def setviewmethod(cls, name):
        viewmethod_name = 'view%s' % name
        viewmethod = lambda self, *a, **kw: ViewItems(self, name, 'view_%s' % name, *a, **kw)
        viewmethod.__doc__ = \
            '"""`%s()` object providing a view on %s"""' % (viewmethod_name, name)
        setattr(cls, viewmethod_name, viewmethod)

    def setitermethod(cls, name):
        itermethod = getattr(cls, name)
        setattr(cls, 'iter%s' % name, itermethod)
        listmethod = lambda self, *a, **kw: list(itermethod(self, *a, **kw))
        listmethod.__doc__ = \
            'Like :py:meth:`iter%s`, but returns a list.' % name
        setattr(cls, name, listmethod)

    def wrap(cls):
        for name in names:
            setitermethod(cls, name)
            setviewmethod(cls, name)
        return cls
    return wrap

Given the following code:

>>> from werkzeug.datastructures import ImmutableMultiDict
>>> a = ImmutableMultiDict({'a':1,'b':2})
>>> a.values()

Under Python 2, this yields [1, 2].

Under Python 3, this yields <generator object values at 0x104a63dc0>.

The former is subscriptable whereas the latter isn't, which explains the error I get.

Ahaha, very nice find, and excellent report!
You can guess from the commit history I haven't used this in a while; I could fix this this weekend probably, unless you want to submit a PR (for which I would be much appreciative!)?

TAGC commented

I'll try.