|
1 | 1 | import contextlib |
| 2 | +import _tokenize |
2 | 3 | import itertools |
3 | 4 | import os |
4 | 5 | import re |
@@ -2273,6 +2274,159 @@ def readline(encoding): |
2273 | 2274 | )) |
2274 | 2275 | self.assertEqual(tokens, expected) |
2275 | 2276 |
|
| 2277 | + def test_stateful_decoder_spans_readline_calls(self): |
| 2278 | + encoded_name = "変数".encode("iso2022_jp") |
| 2279 | + payload = encoded_name[3:-3] |
| 2280 | + lines = iter([ |
| 2281 | + b"# \x1b$B" + payload + b"\n", |
| 2282 | + payload + b"\x1b(B\n", |
| 2283 | + b"", |
| 2284 | + ]) |
| 2285 | + tokens = list(tokenize._generate_tokens_from_c_tokenizer( |
| 2286 | + lines.__next__, |
| 2287 | + extra_tokens=True, |
| 2288 | + encoding="iso2022_jp", |
| 2289 | + )) |
| 2290 | + self.assertEqual(tokens, [ |
| 2291 | + tokenize.TokenInfo( |
| 2292 | + token.COMMENT, "# 変数", (1, 0), (1, 4), "# 変数\n" |
| 2293 | + ), |
| 2294 | + tokenize.TokenInfo(token.NL, "\n", (1, 4), (1, 5), "# 変数\n"), |
| 2295 | + tokenize.TokenInfo(token.NAME, "変数", (2, 0), (2, 2), "変数\n"), |
| 2296 | + tokenize.TokenInfo(token.NEWLINE, "\n", (2, 2), (2, 3), "変数\n"), |
| 2297 | + tokenize.TokenInfo(token.ENDMARKER, "", (3, 0), (3, 0), ""), |
| 2298 | + ]) |
| 2299 | + |
| 2300 | + def test_utf16_bom_in_each_readline_chunk(self): |
| 2301 | + lines = iter([ |
| 2302 | + "x\n".encode("utf-16"), |
| 2303 | + "y\n".encode("utf-16"), |
| 2304 | + b"", |
| 2305 | + ]) |
| 2306 | + tokens = _tokenize.TokenizerIter( |
| 2307 | + lines.__next__, encoding="utf-16", extra_tokens=True |
| 2308 | + ) |
| 2309 | + self.assertEqual(list(tokens), [ |
| 2310 | + (token.NAME, "x", (1, 0), (1, 1), "x\n"), |
| 2311 | + (token.NEWLINE, "\n", (1, 1), (1, 2), "x\n"), |
| 2312 | + (token.NAME, "y", (2, 0), (2, 1), "y\n"), |
| 2313 | + (token.NEWLINE, "\n", (2, 1), (2, 2), "y\n"), |
| 2314 | + (token.ENDMARKER, "", (3, 0), (3, 0), ""), |
| 2315 | + ]) |
| 2316 | + |
| 2317 | + def test_utf8_decoder_spans_readline_calls(self): |
| 2318 | + lines = iter([b"x\xc3", b"\xa9\n", b""]) |
| 2319 | + tokens = list(tokenize._generate_tokens_from_c_tokenizer( |
| 2320 | + lines.__next__, |
| 2321 | + extra_tokens=True, |
| 2322 | + encoding="utf-8", |
| 2323 | + )) |
| 2324 | + self.assertEqual(tokens, [ |
| 2325 | + tokenize.TokenInfo(token.NAME, "xé", (1, 0), (1, 2), "xé\n"), |
| 2326 | + tokenize.TokenInfo(token.NEWLINE, "\n", (1, 2), (1, 3), "xé\n"), |
| 2327 | + tokenize.TokenInfo(token.ENDMARKER, "", (2, 0), (2, 0), ""), |
| 2328 | + ]) |
| 2329 | + |
| 2330 | + def test_utf8_decoder_replaces_incomplete_input_at_eof(self): |
| 2331 | + expected = [ |
| 2332 | + tokenize.TokenInfo(token.NAME, "x�", (1, 0), (1, 2), "x�"), |
| 2333 | + tokenize.TokenInfo(token.NEWLINE, "", (1, 2), (1, 3), "x�"), |
| 2334 | + tokenize.TokenInfo(token.ENDMARKER, "", (2, 0), (2, 0), ""), |
| 2335 | + ] |
| 2336 | + for chunks in ([b"x\xe9", b""], [b"x\xe9"]): |
| 2337 | + with self.subTest(chunks=chunks): |
| 2338 | + lines = iter(chunks) |
| 2339 | + tokens = list(tokenize._generate_tokens_from_c_tokenizer( |
| 2340 | + lines.__next__, |
| 2341 | + extra_tokens=True, |
| 2342 | + encoding="utf-8", |
| 2343 | + )) |
| 2344 | + self.assertEqual(tokens, expected) |
| 2345 | + |
| 2346 | + def test_multiline_readline_chunk(self): |
| 2347 | + expected = [ |
| 2348 | + tokenize.TokenInfo(token.NAME, "x", (1, 0), (1, 1), "x=1\n"), |
| 2349 | + tokenize.TokenInfo(token.OP, "=", (1, 1), (1, 2), "x=1\n"), |
| 2350 | + tokenize.TokenInfo(token.NUMBER, "1", (1, 2), (1, 3), "x=1\n"), |
| 2351 | + tokenize.TokenInfo(token.NEWLINE, "\n", (1, 3), (1, 4), "x=1\n"), |
| 2352 | + tokenize.TokenInfo(token.NAME, "y", (2, 0), (2, 1), "y=2\n"), |
| 2353 | + tokenize.TokenInfo(token.OP, "=", (2, 1), (2, 2), "y=2\n"), |
| 2354 | + tokenize.TokenInfo(token.NUMBER, "2", (2, 2), (2, 3), "y=2\n"), |
| 2355 | + tokenize.TokenInfo(token.NEWLINE, "\n", (2, 3), (2, 4), "y=2\n"), |
| 2356 | + tokenize.TokenInfo(token.ENDMARKER, "", (3, 0), (3, 0), ""), |
| 2357 | + ] |
| 2358 | + lines = iter([b"x=1\ny=2\n", b""]) |
| 2359 | + tokens = list(tokenize._generate_tokens_from_c_tokenizer( |
| 2360 | + lines.__next__, |
| 2361 | + extra_tokens=True, |
| 2362 | + encoding="utf-8", |
| 2363 | + )) |
| 2364 | + self.assertEqual(tokens, expected) |
| 2365 | + |
| 2366 | + def test_multiline_readline_chunk_with_unterminated_tail(self): |
| 2367 | + readline = mock.Mock(side_effect=["x\nz", ""]) |
| 2368 | + iterator = _tokenize.TokenizerIter(readline, extra_tokens=True) |
| 2369 | + expected = [ |
| 2370 | + (token.NAME, "x", (1, 0), (1, 1), "x\n"), |
| 2371 | + (token.NEWLINE, "\n", (1, 1), (1, 2), "x\n"), |
| 2372 | + (token.NAME, "z", (2, 0), (2, 1), "z"), |
| 2373 | + (token.NEWLINE, "", (2, 1), (2, 2), "z"), |
| 2374 | + ] |
| 2375 | + self.assertEqual(readline.call_count, 0) |
| 2376 | + for token_info in expected: |
| 2377 | + self.assertEqual(next(iterator), token_info) |
| 2378 | + self.assertEqual(readline.call_count, 1) |
| 2379 | + self.assertEqual( |
| 2380 | + next(iterator), |
| 2381 | + (token.ENDMARKER, "", (3, 0), (3, 0), ""), |
| 2382 | + ) |
| 2383 | + self.assertEqual(readline.call_count, 2) |
| 2384 | + |
| 2385 | + def test_readline_callback_is_not_read_ahead(self): |
| 2386 | + readline = mock.Mock(side_effect=["x\n", "y\n", ""]) |
| 2387 | + iterator = _tokenize.TokenizerIter(readline, extra_tokens=True) |
| 2388 | + expected = [ |
| 2389 | + ((token.NAME, "x", (1, 0), (1, 1), "x\n"), 1), |
| 2390 | + ((token.NEWLINE, "\n", (1, 1), (1, 2), "x\n"), 1), |
| 2391 | + ((token.NAME, "y", (2, 0), (2, 1), "y\n"), 2), |
| 2392 | + ] |
| 2393 | + self.assertEqual(readline.call_count, 0) |
| 2394 | + for token_info, calls in expected: |
| 2395 | + self.assertEqual(next(iterator), token_info) |
| 2396 | + self.assertEqual(readline.call_count, calls) |
| 2397 | + |
| 2398 | + def test_encoded_readline_replaces_invalid_bytes(self): |
| 2399 | + lines = iter([b"\xff\n", b""]) |
| 2400 | + tokens = list(tokenize._generate_tokens_from_c_tokenizer( |
| 2401 | + lines.__next__, |
| 2402 | + extra_tokens=True, |
| 2403 | + encoding="utf-8", |
| 2404 | + )) |
| 2405 | + self.assertEqual(tokens, [ |
| 2406 | + tokenize.TokenInfo(token.NAME, "�", (1, 0), (1, 1), "�\n"), |
| 2407 | + tokenize.TokenInfo(token.NEWLINE, "\n", (1, 1), (1, 2), "�\n"), |
| 2408 | + tokenize.TokenInfo(token.ENDMARKER, "", (2, 0), (2, 0), ""), |
| 2409 | + ]) |
| 2410 | + |
| 2411 | + def test_stop_iteration_skips_encoded_readline_codec_lookup(self): |
| 2412 | + iterator = _tokenize.TokenizerIter( |
| 2413 | + lambda: b"", |
| 2414 | + extra_tokens=True, |
| 2415 | + encoding="missing-tokenizer-codec", |
| 2416 | + ) |
| 2417 | + with self.assertRaises(LookupError): |
| 2418 | + next(iterator) |
| 2419 | + |
| 2420 | + iterator = _tokenize.TokenizerIter( |
| 2421 | + iter(()).__next__, |
| 2422 | + extra_tokens=True, |
| 2423 | + encoding="missing-tokenizer-codec", |
| 2424 | + ) |
| 2425 | + self.assertEqual( |
| 2426 | + next(iterator), |
| 2427 | + (token.ENDMARKER, "", (1, 0), (1, 0), ""), |
| 2428 | + ) |
| 2429 | + |
2276 | 2430 | def test_extra_tokens_relaxes_lexer_errors(self): |
2277 | 2431 | cases = [ |
2278 | 2432 | ( |
|
0 commit comments