Skip to content

Commit 81b43d7

Browse files
committed
Add more tests and fix some edge cases
1 parent 6f01d08 commit 81b43d7

File tree

3 files changed

+135
-4
lines changed

3 files changed

+135
-4
lines changed

Lib/test/test_exceptions.py

+125
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pickle
99
import weakref
1010
import errno
11+
from textwrap import dedent
1112

1213
from test.support import (captured_stderr, check_impl_detail,
1314
cpython_only, gc_collect,
@@ -1833,6 +1834,130 @@ def test_copy_pickle(self):
18331834
self.assertEqual(exc.name, orig.name)
18341835
self.assertEqual(exc.path, orig.path)
18351836

1837+
class SyntaxErrorTests(unittest.TestCase):
1838+
def test_range_of_offsets(self):
1839+
cases = [
1840+
# Basic range from 2->7
1841+
(("bad.py", 1, 2, "abcdefg", 1, 7),
1842+
dedent(
1843+
"""
1844+
File "bad.py", line 1
1845+
abcdefg
1846+
^^^^^
1847+
SyntaxError: bad bad
1848+
""")),
1849+
# end_offset = start_offset + 1
1850+
(("bad.py", 1, 2, "abcdefg", 1, 3),
1851+
dedent(
1852+
"""
1853+
File "bad.py", line 1
1854+
abcdefg
1855+
^
1856+
SyntaxError: bad bad
1857+
""")),
1858+
# Negative end offset
1859+
(("bad.py", 1, 2, "abcdefg", 1, -2),
1860+
dedent(
1861+
"""
1862+
File "bad.py", line 1
1863+
abcdefg
1864+
^
1865+
SyntaxError: bad bad
1866+
""")),
1867+
# end offset before starting offset
1868+
(("bad.py", 1, 4, "abcdefg", 1, 2),
1869+
dedent(
1870+
"""
1871+
File "bad.py", line 1
1872+
abcdefg
1873+
^
1874+
SyntaxError: bad bad
1875+
""")),
1876+
# Both offsets negative
1877+
(("bad.py", 1, -4, "abcdefg", 1, -2),
1878+
dedent(
1879+
"""
1880+
File "bad.py", line 1
1881+
abcdefg
1882+
SyntaxError: bad bad
1883+
""")),
1884+
# Both offsets negative and the end more negatibe
1885+
(("bad.py", 1, -4, "abcdefg", 1, -5),
1886+
dedent(
1887+
"""
1888+
File "bad.py", line 1
1889+
abcdefg
1890+
SyntaxError: bad bad
1891+
""")),
1892+
# Both offsets 0
1893+
(("bad.py", 1, 0, "abcdefg", 1, 0),
1894+
dedent(
1895+
"""
1896+
File "bad.py", line 1
1897+
abcdefg
1898+
SyntaxError: bad bad
1899+
""")),
1900+
# Start offset 0 and end offset not 0
1901+
(("bad.py", 1, 0, "abcdefg", 1, 5),
1902+
dedent(
1903+
"""
1904+
File "bad.py", line 1
1905+
abcdefg
1906+
SyntaxError: bad bad
1907+
""")),
1908+
# End offset pass the source lenght
1909+
(("bad.py", 1, 2, "abcdefg", 1, 100),
1910+
dedent(
1911+
"""
1912+
File "bad.py", line 1
1913+
abcdefg
1914+
^^^^^^
1915+
SyntaxError: bad bad
1916+
""")),
1917+
]
1918+
for args, expected in cases:
1919+
with self.subTest(args=args):
1920+
try:
1921+
raise SyntaxError("bad bad", args)
1922+
except SyntaxError as exc:
1923+
with support.captured_stderr() as err:
1924+
sys.__excepthook__(*sys.exc_info())
1925+
the_exception = exc
1926+
1927+
def test_attributes_new_constructor(self):
1928+
args = ("bad.py", 1, 2, "abcdefg", 1, 100)
1929+
the_exception = SyntaxError("bad bad", args)
1930+
filename, lineno, offset, error, end_lineno, end_offset = args
1931+
self.assertEqual(filename, the_exception.filename)
1932+
self.assertEqual(lineno, the_exception.lineno)
1933+
self.assertEqual(end_lineno, the_exception.end_lineno)
1934+
self.assertEqual(offset, the_exception.offset)
1935+
self.assertEqual(end_offset, the_exception.end_offset)
1936+
self.assertEqual(error, the_exception.text)
1937+
self.assertEqual("bad bad", the_exception.msg)
1938+
1939+
def test_attributes_old_constructor(self):
1940+
args = ("bad.py", 1, 2, "abcdefg")
1941+
the_exception = SyntaxError("bad bad", args)
1942+
filename, lineno, offset, error = args
1943+
self.assertEqual(filename, the_exception.filename)
1944+
self.assertEqual(lineno, the_exception.lineno)
1945+
self.assertEqual(None, the_exception.end_lineno)
1946+
self.assertEqual(offset, the_exception.offset)
1947+
self.assertEqual(None, the_exception.end_offset)
1948+
self.assertEqual(error, the_exception.text)
1949+
self.assertEqual("bad bad", the_exception.msg)
1950+
1951+
def test_incorrect_constructor(self):
1952+
args = ("bad.py", 1, 2)
1953+
self.assertRaises(TypeError, SyntaxError, "bad bad", args)
1954+
1955+
args = ("bad.py", 1, 2, 4, 5, 6, 7)
1956+
self.assertRaises(TypeError, SyntaxError, "bad bad", args)
1957+
1958+
args = ("bad.py", 1, 2, "abcdefg", 1)
1959+
self.assertRaises(TypeError, SyntaxError, "bad bad", args)
1960+
18361961

18371962
class PEP626Tests(unittest.TestCase):
18381963

Objects/exceptions.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -1494,8 +1494,9 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
14941494
if (lenargs == 2) {
14951495
info = PyTuple_GET_ITEM(args, 1);
14961496
info = PySequence_Tuple(info);
1497-
if (!info)
1497+
if (!info) {
14981498
return -1;
1499+
}
14991500

15001501
self->end_lineno = NULL;
15011502
self->end_offset = NULL;
@@ -1505,7 +1506,7 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
15051506
&self->end_lineno, &self->end_offset)) {
15061507
Py_DECREF(info);
15071508
return -1;
1508-
}
1509+
}
15091510

15101511
Py_INCREF(self->filename);
15111512
Py_INCREF(self->lineno);
@@ -1515,6 +1516,11 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
15151516
Py_XINCREF(self->end_offset);
15161517
Py_DECREF(info);
15171518

1519+
if (self->end_lineno != NULL && self->end_offset == NULL) {
1520+
PyErr_SetString(PyExc_TypeError, "end_offset must be provided when end_lineno is provided");
1521+
return -1;
1522+
}
1523+
15181524
/*
15191525
* Issue #21669: Custom error for 'print' & 'exec' as statements
15201526
*

Python/pythonrun.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ parse_syntax_error(PyObject *err, PyObject **message, PyObject **filename,
628628
static void
629629
print_error_text(PyObject *f, Py_ssize_t offset, Py_ssize_t end_offset, PyObject *text_obj)
630630
{
631-
size_t caret_repetitions = end_offset > 0 && end_offset != offset ? end_offset - offset : 1;
631+
size_t caret_repetitions = (end_offset > 0 && end_offset > offset) ? end_offset - offset : 1;
632632
/* Convert text to a char pointer; return if error */
633633
const char *text = PyUnicode_AsUTF8(text_obj);
634634
if (text == NULL)
@@ -945,7 +945,7 @@ print_exception(PyObject *f, PyObject *value)
945945
// Limit the ammount of '^' that we can display to
946946
// the size of the text in the source line.
947947
if (error_line != NULL && end_offset > line_size + 1) {
948-
end_offset = line_size;
948+
end_offset = line_size + 1;
949949
}
950950
print_error_text(f, offset, end_offset, text);
951951
Py_DECREF(text);

0 commit comments

Comments
 (0)