13

I'm using Flask with Python 3.3 and I know support is still experimental but I'm running into errors when trying to test file uploads. I'm using unittest.TestCase and based on Python 2.7 examples I've seen in the docs I'm trying

rv = self.app.post('/add', data=dict(
                               file=(io.StringIO("this is a test"), 'test.pdf'),
                           ), follow_redirects=True)

and getting

TypeError: 'str' does not support the buffer interface

I've tried a few variations around io.StringIO but can't find anything that works. Any help is much appreciated!

The full stack trace is

Traceback (most recent call last):
  File "archive_tests.py", line 44, in test_add_transcript
    ), follow_redirects=True)
  File "/srv/transcript_archive/py3env/lib/python3.3/site-packages/werkzeug/test.py", line 771, in post
    return self.open(*args, **kw)
  File "/srv/transcript_archive/py3env/lib/python3.3/site-packages/flask/testing.py", line 108, in open
    follow_redirects=follow_redirects)
  File "/srv/transcript_archive/py3env/lib/python3.3/site-packages/werkzeug/test.py", line 725, in open
    environ = args[0].get_environ()
  File "/srv/transcript_archive/py3env/lib/python3.3/site-packages/werkzeug/test.py", line 535, in get_environ
    stream_encode_multipart(values, charset=self.charset)
  File "/srv/transcript_archive/py3env/lib/python3.3/site-packages/werkzeug/test.py", line 98, in stream_encode_multipart
    write_binary(chunk)
  File "/srv/transcript_archive/py3env/lib/python3.3/site-packages/werkzeug/test.py", line 59, in write_binary
    stream.write(string)
TypeError: 'str' does not support the buffer interface
steverippl
  • 378
  • 4
  • 15
  • What is the full traceback? Why are you using `io.StringIO()` to post, could it be that you need a binary file object here? `io.BytesIO(b'this is a test')` might work, for example. – Martijn Pieters Nov 19 '13 at 19:17
  • Looking at the [`werkzeug` `FileStorage` object](https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/datastructures.py) (which handles file uploads in a test) I don't see anything that'll give me enough information. You'll need to add the **full** traceback for anyone to be able to say anything useful. – Martijn Pieters Nov 19 '13 at 19:32
  • Sorry, added the trace. I'm using `io.String` because the werkzeug examples use `StringIO` in their examples [link](http://werkzeug.pocoo.org/docs/test/). Using `io.BytesIO` made no difference. – steverippl Nov 19 '13 at 19:39
  • 2
    `StringIO` in Python 2 handles *byte strings*, not Unicode. The Python 3 equivalent is `io.BytesIO`. – Martijn Pieters Nov 19 '13 at 19:40
  • 1
    OK, `file=(io.BytesIO(b"this is a test"), 'test.pdf')` does work, I had forgotten to put the `b` in front of the quoted text when I tested after your initial response. Thanks! – steverippl Nov 19 '13 at 19:45
  • I was *just* busy typing in an incredulous response; reading the `werkzeug.test` module made it clear it is writing the files over to a `BytesIO` object, so it'll expect a binary file object. – Martijn Pieters Nov 19 '13 at 19:46
  • I've typed this up as an answer below, for future visitors. – Martijn Pieters Nov 19 '13 at 19:49

1 Answers1

19

In Python 3, you need to use io.BytesIO() (with a bytes value) to simulate an uploaded file:

rv = self.app.post('/add', data=dict(
                               file=(io.BytesIO(b"this is a test"), 'test.pdf'),
                           ), follow_redirects=True)

Note the b'...' string defining a bytes literal.

In the Python 2 test examples, the StringIO() object holds a byte string, not a unicode value, and in Python 3, io.BytesIO() is the equivalent.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343