@@ -1551,6 +1551,19 @@ def _run_frontend(profile: Path) -> dict[str, object]:
15511551 return json .loads (result .stdout .strip ().splitlines ()[- 1 ])
15521552
15531553
1554+ def _both_ends (text : str , * , head : int = 3000 , tail : int = 2000 ) -> str :
1555+ """Keep a long diagnostic's beginning as well as its end.
1556+
1557+ A tail slice is the wrong half for a native crash: ``faulthandler`` prints
1558+ the exception and the faulting thread *first*, so #838 has twice been
1559+ reported with everything below it and the fault location cut off.
1560+ """
1561+ if len (text ) <= head + tail :
1562+ return text
1563+ dropped = len (text ) - head - tail
1564+ return f"{ text [:head ]} \n [... { dropped } characters ...]\n { text [- tail :]} "
1565+
1566+
15541567def _reap_frontends (frontends : list [subprocess .Popen [str ]]) -> None :
15551568 """Kill direct frontends together, then reap them without reading pipes."""
15561569 for frontend in frontends :
@@ -1640,6 +1653,28 @@ def _stop(pid: object) -> None:
16401653 os .kill (pid , signal .SIGKILL )
16411654
16421655
1656+ def test_a_native_crash_report_keeps_the_faulting_thread ():
1657+ # Twice now (#838) a Windows access violation has been reported with the
1658+ # fault cut off, because the assertion kept the last 2000 characters and
1659+ # faulthandler prints the faulting thread before every other one.
1660+ crash = (
1661+ "Windows fatal exception: access violation\n \n "
1662+ "Current thread 0x00001734 (most recent call first):\n "
1663+ ' File "daemon_election.py", line 706 in collect\n '
1664+ + "" .join (
1665+ f' File "threading.py", line { line } in run\n ' for line in range (4000 )
1666+ )
1667+ + "the last line of the report\n "
1668+ )
1669+
1670+ kept = _both_ends (crash )
1671+
1672+ assert "Current thread 0x00001734" in kept
1673+ assert "line 706 in collect" in kept
1674+ assert kept .endswith ("the last line of the report\n " )
1675+ assert len (kept ) < len (crash )
1676+
1677+
16431678@pytest .mark .parametrize (("exit_code" , "expected" ), [(259 , True ), (7 , False )])
16441679def test_windows_owner_liveness_is_a_query (
16451680 exit_code : int , expected : bool , monkeypatch : pytest .MonkeyPatch
@@ -4770,10 +4805,10 @@ def track_stop(pid: object) -> None:
47704805 @pytest .mark .skipif (
47714806 os .name == "nt"
47724807 and sys .implementation .name == "cpython"
4773- and sys .version_info [:3 ] == (3 , 12 , 4 ),
4808+ and sys .version_info [:2 ] == (3 , 12 ),
47744809 reason = (
4775- "CPython 3.12.4 intermittently access-violates under this "
4776- "daemon-thread I/O stress"
4810+ "CPython 3.12 intermittently access-violates under this "
4811+ "daemon-thread I/O stress (#838) "
47774812 ),
47784813 )
47794814 def test_many_clients_starting_at_once_elect_exactly_one_owner (
@@ -4833,7 +4868,7 @@ def test_many_clients_starting_at_once_elect_exactly_one_owner(
48334868
48344869 for frontend in running :
48354870 out , err = frontend .communicate (timeout = 300 )
4836- assert frontend .returncode == 0 , err [ - 2000 :]
4871+ assert frontend .returncode == 0 , _both_ends ( err )
48374872 result = json .loads (out .strip ().splitlines ()[- 1 ])
48384873 results .append (result )
48394874 owners .add (result ["pid" ])
0 commit comments