|
1 | 1 | # XXX TypeErrors on calling handlers, or on bad return values from a
|
2 | 2 | # handler, are obscure and unhelpful.
|
3 | 3 |
|
4 |
| -from io import BytesIO |
5 | 4 | import os
|
6 | 5 | import platform
|
7 | 6 | import sys
|
8 | 7 | import sysconfig
|
9 | 8 | import unittest
|
10 | 9 | import traceback
|
| 10 | +from io import BytesIO |
| 11 | +from test import support |
| 12 | +from test.support import os_helper |
11 | 13 |
|
12 | 14 | from xml.parsers import expat
|
13 | 15 | from xml.parsers.expat import errors
|
@@ -439,37 +441,59 @@ def test7(self):
|
439 | 441 | # Test handling of exception from callback:
|
440 | 442 | class HandlerExceptionTest(unittest.TestCase):
|
441 | 443 | def StartElementHandler(self, name, attrs):
|
442 |
| - raise RuntimeError(name) |
| 444 | + raise RuntimeError(f'StartElementHandler: <{name}>') |
443 | 445 |
|
444 | 446 | def check_traceback_entry(self, entry, filename, funcname):
|
445 |
| - self.assertEqual(os.path.basename(entry[0]), filename) |
446 |
| - self.assertEqual(entry[2], funcname) |
| 447 | + self.assertEqual(os.path.basename(entry.filename), filename) |
| 448 | + self.assertEqual(entry.name, funcname) |
447 | 449 |
|
| 450 | + @support.cpython_only |
448 | 451 | def test_exception(self):
|
| 452 | + # gh-66652: test _PyTraceback_Add() used by pyexpat.c to inject frames |
| 453 | + |
| 454 | + # Change the current directory to the Python source code directory |
| 455 | + # if it is available. |
| 456 | + src_dir = sysconfig.get_config_var('abs_builddir') |
| 457 | + if src_dir: |
| 458 | + have_source = os.path.isdir(src_dir) |
| 459 | + else: |
| 460 | + have_source = False |
| 461 | + if have_source: |
| 462 | + with os_helper.change_cwd(src_dir): |
| 463 | + self._test_exception(have_source) |
| 464 | + else: |
| 465 | + self._test_exception(have_source) |
| 466 | + |
| 467 | + def _test_exception(self, have_source): |
| 468 | + # Use path relative to the current directory which should be the Python |
| 469 | + # source code directory (if it is available). |
| 470 | + PYEXPAT_C = os.path.join('Modules', 'pyexpat.c') |
| 471 | + |
449 | 472 | parser = expat.ParserCreate()
|
450 | 473 | parser.StartElementHandler = self.StartElementHandler
|
451 | 474 | try:
|
452 | 475 | parser.Parse(b"<a><b><c/></b></a>", True)
|
453 |
| - self.fail() |
454 |
| - except RuntimeError as e: |
455 |
| - self.assertEqual(e.args[0], 'a', |
456 |
| - "Expected RuntimeError for element 'a', but" + \ |
457 |
| - " found %r" % e.args[0]) |
458 |
| - # Check that the traceback contains the relevant line in pyexpat.c |
459 |
| - entries = traceback.extract_tb(e.__traceback__) |
460 |
| - self.assertEqual(len(entries), 3) |
461 |
| - self.check_traceback_entry(entries[0], |
462 |
| - "test_pyexpat.py", "test_exception") |
463 |
| - self.check_traceback_entry(entries[1], |
464 |
| - "pyexpat.c", "StartElement") |
465 |
| - self.check_traceback_entry(entries[2], |
466 |
| - "test_pyexpat.py", "StartElementHandler") |
467 |
| - if (sysconfig.is_python_build() |
468 |
| - and not (sys.platform == 'win32' and platform.machine() == 'ARM') |
469 |
| - and not is_emscripten |
470 |
| - and not is_wasi |
471 |
| - ): |
472 |
| - self.assertIn('call_with_frame("StartElement"', entries[1][3]) |
| 476 | + |
| 477 | + self.fail("the parser did not raise RuntimeError") |
| 478 | + except RuntimeError as exc: |
| 479 | + self.assertEqual(exc.args[0], 'StartElementHandler: <a>', exc) |
| 480 | + entries = traceback.extract_tb(exc.__traceback__) |
| 481 | + |
| 482 | + self.assertEqual(len(entries), 3, entries) |
| 483 | + self.check_traceback_entry(entries[0], |
| 484 | + "test_pyexpat.py", "_test_exception") |
| 485 | + self.check_traceback_entry(entries[1], |
| 486 | + os.path.basename(PYEXPAT_C), |
| 487 | + "StartElement") |
| 488 | + self.check_traceback_entry(entries[2], |
| 489 | + "test_pyexpat.py", "StartElementHandler") |
| 490 | + |
| 491 | + # Check that the traceback contains the relevant line in |
| 492 | + # Modules/pyexpat.c. Skip the test if Modules/pyexpat.c is not |
| 493 | + # available. |
| 494 | + if have_source and os.path.exists(PYEXPAT_C): |
| 495 | + self.assertIn('call_with_frame("StartElement"', |
| 496 | + entries[1].line) |
473 | 497 |
|
474 | 498 |
|
475 | 499 | # Test Current* members:
|
|
0 commit comments