You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Clarify error when ‘tokenize.detect_encoding’ receives text #67486
So, on catching StopIteration, the return value will be a byte string. The detect_encoding code then immediately calls sartswith, which fails::
File "/usr/lib/python3.4/tokenize.py", line 409, in detect_encoding
if first.startswith(BOM_UTF8):
TypeError: startswith first arg must be str or a tuple of str, not bytes
One or both of those locations in the code is wrong. Either read_or_stop should never return a byte string; or detect_encoding should not assume it can call startswith on the result.
I don't understand why do you consider that this issue is a bug. Can you show an example where detect_encoding() raises an exception?
Pod commented on Feb 12, 2015
Podmannequin
Mannequin
More actions
Not the OP, but I find this message a bug because it's confusing from the perspective of a user of the tokenize() function. If you give tokenize a readlines() that returns a str, you get this error message that confusingly states that something inside tokenize must be a string and NOT a bytes, even though the user gave readlines a string, not a bytes. It looks like an internal bug.
Turns out it's because the contact changed from python2 to 3.
Personally, I'd been accidentally reading the python2 page for the tokenize library instead of python3, and had been using tokenize.generate_tokens in my python 3 code which accepts a io.StringIO just fine. When I realising my mistake and switched to the python3 version of the page I noticed generate_tokens is no longer supported, even though the code I had was working, and I noticed that the definition of tokenize had changed to match the old generate_tokens (along with a subtle change in the definition of the acceptable readlines function).
So when I switched from tokenize.generate_tokens to tokenize.tokenize to try and use the library as intended, I get the same error as OP. Perhaps OP made a similar mistake?
To actually hit the error in question:
$ cat -n temp.py
1import tokenize
2import io
345 byte_reader = io.BytesIO(b"test bytes generate_tokens")
6 tokens = tokenize.generate_tokens(byte_reader.readline)
78 byte_reader = io.BytesIO(b"test bytes tokenize")
9 tokens = tokenize.tokenize(byte_reader.readline)
1011 byte_reader = io.StringIO("test string generate")
12 tokens = tokenize.generate_tokens(byte_reader.readline)
1314 str_reader = io.StringIO("test string tokenize")
15 tokens = tokenize.tokenize(str_reader.readline)
1617$ python3 temp.py
Traceback (most recent call last):
File "temp.py", line 15, in<module>
tokens = tokenize.tokenize(str_reader.readline)
File "C:\work\env\python\Python34_64\Lib\tokenize.py", line 467, in tokenize
encoding, consumed = detect_encoding(readline)
File "C:\work\env\python\Python34_64\Lib\tokenize.py", line 409, in detect_encoding
if first.startswith(BOM_UTF8):
TypeError: startswith first arg must be stror a tuple of str, notbytes
The error message could indeed be made clearer by turning it into a message that tokenize itself requires bytes input. Or, more likely, the additional error handling needs to be in detect_encoding.
changed the title [-]‘tokenize.detect_encoding’ is confused between text and bytes: no ‘startswith’ method on a byte string[/-][+]Clarify error when ‘tokenize.detect_encoding’ receives text[/+]on Jun 21, 2016
When we pass f.readline to tokenize.tokenize(), the second call to detect_encoding() fails, because f.readline() returns str.
In Lib/test/test_tokenize.py, it seems like tokenize.open() is only tested to open a file. Its output isn't passed to tokenize.tokenize(). Most of the tests either pass the readline() method of open(..., 'rb') or io.BytesIO() to tokenize.tokenize().
I will submit a documentation PR that suggests to use tokenize.generate_tokens() with tokenize.open().
Activity
bignose commentedon Jan 22, 2015
In
tokenize.detect_encodingis the following code::The
read_or_stopfunction is defined as::So, on catching
StopIteration, the return value will be a byte string. Thedetect_encodingcode then immediately callssartswith, which fails::TypeError: startswith first arg must be str or a tuple of str, not bytes
One or both of those locations in the code is wrong. Either
read_or_stopshould never return a byte string; ordetect_encodingshould not assume it can callstartswithon the result.bignose commentedon Jan 22, 2015
Possibly related to bpo-9969.
bitdancer commentedon Jan 22, 2015
bytes does support startswith:
vstinner commentedon Jan 22, 2015
I don't understand why do you consider that this issue is a bug. Can you show an example where detect_encoding() raises an exception?
Pod commentedon Feb 12, 2015
Not the OP, but I find this message a bug because it's confusing from the perspective of a user of the tokenize() function. If you give tokenize a readlines() that returns a str, you get this error message that confusingly states that something inside tokenize must be a string and NOT a bytes, even though the user gave readlines a string, not a bytes. It looks like an internal bug.
Turns out it's because the contact changed from python2 to 3.
Personally, I'd been accidentally reading the python2 page for the tokenize library instead of python3, and had been using tokenize.generate_tokens in my python 3 code which accepts a io.StringIO just fine. When I realising my mistake and switched to the python3 version of the page I noticed generate_tokens is no longer supported, even though the code I had was working, and I noticed that the definition of tokenize had changed to match the old generate_tokens (along with a subtle change in the definition of the acceptable readlines function).
So when I switched from tokenize.generate_tokens to tokenize.tokenize to try and use the library as intended, I get the same error as OP. Perhaps OP made a similar mistake?
To actually hit the error in question:
bitdancer commentedon Feb 20, 2015
The error message could indeed be made clearer by turning it into a message that tokenize itself requires bytes input. Or, more likely, the additional error handling needs to be in detect_encoding.
[-]‘tokenize.detect_encoding’ is confused between text and bytes: no ‘startswith’ method on a byte string[/-][+]Clarify error when ‘tokenize.detect_encoding’ receives text[/+]berkerpeksag commentedon Oct 12, 2016
It looks like this can also be fixed by bpo-12486.
berkerpeksag commentedon Apr 28, 2019
The original problem has already been solved by making tokenize.generate_tokens() public in bpo-12486.
However, the same exception can be raised when tokenize.open() is used with tokenize.tokenize(), because it returns a text stream:
hello.py
--------
text.py
-------
When we pass f.readline to tokenize.tokenize(), the second call to detect_encoding() fails, because f.readline() returns str.
In Lib/test/test_tokenize.py, it seems like tokenize.open() is only tested to open a file. Its output isn't passed to tokenize.tokenize(). Most of the tests either pass the readline() method of open(..., 'rb') or io.BytesIO() to tokenize.tokenize().
I will submit a documentation PR that suggests to use tokenize.generate_tokens() with tokenize.open().
bignose commentedon Apr 28, 2019
On 28-Apr-2019, Berker Peksag wrote:
I don't understand how that would affect the resolution of this issue.
Isn't the correct resolution here going to entail correct
implementation in ‘file.readline’?