Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 686051c

Browse files
fix(traceback): TracebackException to handle error messages with punctuations
docs: add news entry for TracebackException punctuation fix style: remove trailing whitespace in traceback.py Update 2025-08-24-11-42-38.gh-issue-137716.7-Mtj-.rst Update 2025-08-24-11-42-38.gh-issue-137716.7-Mtj-.rst refactor: improve suggestion message computation and punctuation handling style: remove trailing whitespace in traceback.py gh-137986: Fix and improve the csv functions docstrings (GH-137987) The csv.register_dialect() docstring no longer imply that it returns a dialect. All functions have now signatures. Co-authored-by: maurycy <[email protected]> gh-135261: bring back CI job for testing OpenSSL 1.1.1w (#135262) This partially reverts commit d83e30c by bringing back the CI job for testing OpenSSL 1.1.1w. Despite this version being upstream EOL, the rationale for keeping it as follows: - It most resembles other 1.1.1-work-a-like ssl APIs supported by important vendors. - Python officially requires OpenSSL 1.1.1 or later, although OpenSSL 3.0 or later is recommended for cryptographic modules. Since changing the build requirements requires a transition period, we need to keep testing the allowed versions. - The code base still contains calls to OpenSSL functions that are deprecated since OpenSSL 3.0 as well as `ifdef` blocks constrained to OpenSSL 1.1.1.
1 parent 6fcac09 commit 686051c

File tree

6 files changed

+98
-93
lines changed

6 files changed

+98
-93
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,10 @@ jobs:
270270
fail-fast: false
271271
matrix:
272272
os: [ubuntu-24.04]
273-
openssl_ver: [3.0.17, 3.2.5, 3.3.4, 3.4.2, 3.5.2]
273+
# Keep 1.1.1w in our list despite it being upstream EOL and otherwise
274+
# unsupported as it most resembles other 1.1.1-work-a-like ssl APIs
275+
# supported by important vendors such as AWS-LC.
276+
openssl_ver: [1.1.1w, 3.0.17, 3.2.5, 3.3.4, 3.4.2, 3.5.2]
274277
# See Tools/ssl/make_ssl_data.py for notes on adding a new version
275278
env:
276279
OPENSSL_VER: ${{ matrix.openssl_ver }}

Doc/library/csv.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ The :mod:`csv` module defines the following functions:
113113
spamwriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam'])
114114

115115

116-
.. function:: register_dialect(name[, dialect[, **fmtparams]])
116+
.. function:: register_dialect(name, /, dialect='excel', **fmtparams)
117117

118118
Associate *dialect* with *name*. *name* must be a string. The
119119
dialect can be specified either by passing a sub-class of :class:`Dialect`, or
@@ -139,7 +139,8 @@ The :mod:`csv` module defines the following functions:
139139
Return the names of all registered dialects.
140140

141141

142-
.. function:: field_size_limit([new_limit])
142+
.. function:: field_size_limit()
143+
field_size_limit(new_limit)
143144

144145
Returns the current maximum field size allowed by the parser. If *new_limit* is
145146
given, this becomes the new limit.
@@ -526,7 +527,7 @@ out surrounded by parens. This may cause some problems for other programs which
526527
read CSV files (assuming they support complex numbers at all).
527528

528529

529-
.. method:: csvwriter.writerow(row)
530+
.. method:: csvwriter.writerow(row, /)
530531

531532
Write the *row* parameter to the writer's file object, formatted according
532533
to the current :class:`Dialect`. Return the return value of the call to the
@@ -535,7 +536,7 @@ read CSV files (assuming they support complex numbers at all).
535536
.. versionchanged:: 3.5
536537
Added support of arbitrary iterables.
537538

538-
.. method:: csvwriter.writerows(rows)
539+
.. method:: csvwriter.writerows(rows, /)
539540

540541
Write all elements in *rows* (an iterable of *row* objects as described
541542
above) to the writer's file object, formatted according to the current

Lib/traceback.py

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,50 +1081,33 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
10811081

10821082
self._is_syntax_error = False
10831083
self._have_exc_type = exc_type is not None
1084-
if exc_type is not None:
1084+
1085+
if self._have_exc_type:
10851086
self.exc_type_qualname = exc_type.__qualname__
10861087
self.exc_type_module = exc_type.__module__
1088+
if issubclass(exc_type, SyntaxError):
1089+
# Handle SyntaxError's specially
1090+
self.filename = exc_value.filename
1091+
lno = exc_value.lineno
1092+
self.lineno = str(lno) if lno is not None else None
1093+
end_lno = exc_value.end_lineno
1094+
self.end_lineno = str(end_lno) if end_lno is not None else None
1095+
self.text = exc_value.text
1096+
self.offset = exc_value.offset
1097+
self.end_offset = exc_value.end_offset
1098+
self.msg = exc_value.msg
1099+
self._is_syntax_error = True
1100+
self._exc_metadata = getattr(exc_value, "_metadata", None)
1101+
elif suggestion := _compute_suggestion_message(exc_type, exc_value, exc_traceback):
1102+
if self._str.endswith(('.', '?', '!', '...')):
1103+
punctuation = ''
1104+
else:
1105+
punctuation = '.'
1106+
self._str += f"{punctuation} {suggestion}"
10871107
else:
10881108
self.exc_type_qualname = None
10891109
self.exc_type_module = None
10901110

1091-
if exc_type and issubclass(exc_type, SyntaxError):
1092-
# Handle SyntaxError's specially
1093-
self.filename = exc_value.filename
1094-
lno = exc_value.lineno
1095-
self.lineno = str(lno) if lno is not None else None
1096-
end_lno = exc_value.end_lineno
1097-
self.end_lineno = str(end_lno) if end_lno is not None else None
1098-
self.text = exc_value.text
1099-
self.offset = exc_value.offset
1100-
self.end_offset = exc_value.end_offset
1101-
self.msg = exc_value.msg
1102-
self._is_syntax_error = True
1103-
self._exc_metadata = getattr(exc_value, "_metadata", None)
1104-
elif exc_type and issubclass(exc_type, ImportError) and \
1105-
getattr(exc_value, "name_from", None) is not None:
1106-
wrong_name = getattr(exc_value, "name_from", None)
1107-
suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
1108-
if suggestion:
1109-
self._str += f". Did you mean: '{suggestion}'?"
1110-
elif exc_type and issubclass(exc_type, ModuleNotFoundError) and \
1111-
sys.flags.no_site and \
1112-
getattr(exc_value, "name", None) not in sys.stdlib_module_names:
1113-
self._str += (". Site initialization is disabled, did you forget to "
1114-
+ "add the site-packages directory to sys.path?")
1115-
elif exc_type and issubclass(exc_type, (NameError, AttributeError)) and \
1116-
getattr(exc_value, "name", None) is not None:
1117-
wrong_name = getattr(exc_value, "name", None)
1118-
suggestion = _compute_suggestion_error(exc_value, exc_traceback, wrong_name)
1119-
if suggestion:
1120-
self._str += f". Did you mean: '{suggestion}'?"
1121-
if issubclass(exc_type, NameError):
1122-
wrong_name = getattr(exc_value, "name", None)
1123-
if wrong_name is not None and wrong_name in sys.stdlib_module_names:
1124-
if suggestion:
1125-
self._str += f" Or did you forget to import '{wrong_name}'?"
1126-
else:
1127-
self._str += f". Did you forget to import '{wrong_name}'?"
11281111
if lookup_lines:
11291112
self._load_lines()
11301113
self.__suppress_context__ = \
@@ -1384,7 +1367,6 @@ def _find_keyword_typos(self):
13841367
self.msg = f"invalid syntax. Did you mean '{suggestion}'?"
13851368
return
13861369

1387-
13881370
def _format_syntax_error(self, stype, **kwargs):
13891371
"""Format SyntaxError exceptions (internal helper)."""
13901372
# Show exactly where the problem was found.
@@ -1577,7 +1559,6 @@ def format(self, *, chain=True, _ctx=None, **kwargs):
15771559
assert _ctx.exception_group_depth == 1
15781560
_ctx.exception_group_depth = 0
15791561

1580-
15811562
def print(self, *, file=None, chain=True, **kwargs):
15821563
"""Print the result of self.format(chain=chain) to 'file'."""
15831564
colorize = kwargs.get("colorize", False)
@@ -1732,6 +1713,38 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
17321713
return suggestion
17331714

17341715

1716+
def _compute_suggestion_message(exc_type, exc_value, exc_traceback):
1717+
if (
1718+
issubclass(exc_type, ModuleNotFoundError)
1719+
and sys.flags.no_site
1720+
and getattr(exc_value, "name", None) not in sys.stdlib_module_names
1721+
):
1722+
return ("Site initialization is disabled, did you forget to "
1723+
"add the site-packages directory to sys.path?")
1724+
if issubclass(exc_type, (ImportError, NameError, AttributeError)):
1725+
if issubclass(exc_type, ImportError):
1726+
wrong_name = getattr(exc_value, "name_from", None)
1727+
else:
1728+
wrong_name = getattr(exc_value, "name", None)
1729+
if wrong_name:
1730+
other_name = _compute_suggestion_error(
1731+
exc_value, exc_traceback, wrong_name
1732+
)
1733+
maybe_builtin_import = (
1734+
issubclass(exc_type, NameError)
1735+
and wrong_name in sys.stdlib_module_names
1736+
)
1737+
if not other_name:
1738+
if maybe_builtin_import:
1739+
return f"Did you forget to import '{wrong_name}'?"
1740+
return None
1741+
text = f"Did you mean: '{other_name}'?"
1742+
if maybe_builtin_import:
1743+
return f"{text} Or did you forget to import '{wrong_name}'?"
1744+
return text
1745+
return None
1746+
1747+
17351748
def _levenshtein_distance(a, b, max_cost):
17361749
# A Python implementation of Python/suggestions.c:levenshtein_distance.
17371750

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid double punctuation in :class:`~traceback.TracebackException` messages

Modules/_csv.c

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,10 +1304,11 @@ join_append_lineterminator(WriterObj *self)
13041304
}
13051305

13061306
PyDoc_STRVAR(csv_writerow_doc,
1307-
"writerow(iterable)\n"
1307+
"writerow($self, row, /)\n"
1308+
"--\n\n"
1309+
"Construct and write a CSV record from an iterable of fields.\n"
13081310
"\n"
1309-
"Construct and write a CSV record from an iterable of fields. Non-string\n"
1310-
"elements will be converted to string.");
1311+
"Non-string elements will be converted to string.");
13111312

13121313
static PyObject *
13131314
csv_writerow(PyObject *op, PyObject *seq)
@@ -1414,10 +1415,11 @@ csv_writerow(PyObject *op, PyObject *seq)
14141415
}
14151416

14161417
PyDoc_STRVAR(csv_writerows_doc,
1417-
"writerows(iterable of iterables)\n"
1418+
"writerows($self, rows, /)\n"
1419+
"--\n\n"
1420+
"Construct and write a series of iterables to a csv file.\n"
14181421
"\n"
1419-
"Construct and write a series of iterables to a csv file. Non-string\n"
1420-
"elements will be converted to string.");
1422+
"Non-string elements will be converted to string.");
14211423

14221424
static PyObject *
14231425
csv_writerows(PyObject *self, PyObject *seqseq)
@@ -1574,13 +1576,11 @@ csv_writer(PyObject *module, PyObject *args, PyObject *keyword_args)
15741576
_csv.list_dialects
15751577
15761578
Return a list of all known dialect names.
1577-
1578-
names = csv.list_dialects()
15791579
[clinic start generated code]*/
15801580

15811581
static PyObject *
15821582
_csv_list_dialects_impl(PyObject *module)
1583-
/*[clinic end generated code: output=a5b92b215b006a6d input=8953943eb17d98ab]*/
1583+
/*[clinic end generated code: output=a5b92b215b006a6d input=ec58040aafd6a20a]*/
15841584
{
15851585
return PyDict_Keys(get_csv_state(module)->dialects);
15861586
}
@@ -1617,13 +1617,11 @@ _csv.unregister_dialect
16171617
name: object
16181618
16191619
Delete the name/dialect mapping associated with a string name.
1620-
1621-
csv.unregister_dialect(name)
16221620
[clinic start generated code]*/
16231621

16241622
static PyObject *
16251623
_csv_unregister_dialect_impl(PyObject *module, PyObject *name)
1626-
/*[clinic end generated code: output=0813ebca6c058df4 input=6b5c1557bf60c7e7]*/
1624+
/*[clinic end generated code: output=0813ebca6c058df4 input=e1cf81bfe3ba0f62]*/
16271625
{
16281626
_csvstate *module_state = get_csv_state(module);
16291627
int rc = PyDict_Pop(module_state->dialects, name, NULL);
@@ -1643,13 +1641,11 @@ _csv.get_dialect
16431641
name: object
16441642
16451643
Return the dialect instance associated with name.
1646-
1647-
dialect = csv.get_dialect(name)
16481644
[clinic start generated code]*/
16491645

16501646
static PyObject *
16511647
_csv_get_dialect_impl(PyObject *module, PyObject *name)
1652-
/*[clinic end generated code: output=aa988cd573bebebb input=edf9ddab32e448fb]*/
1648+
/*[clinic end generated code: output=aa988cd573bebebb input=74865c659dcb441f]*/
16531649
{
16541650
return get_dialect_from_registry(name, get_csv_state(module));
16551651
}
@@ -1661,15 +1657,13 @@ _csv.field_size_limit
16611657
16621658
Sets an upper limit on parsed fields.
16631659
1664-
csv.field_size_limit([limit])
1665-
16661660
Returns old limit. If limit is not given, no new limit is set and
16671661
the old limit is returned
16681662
[clinic start generated code]*/
16691663

16701664
static PyObject *
16711665
_csv_field_size_limit_impl(PyObject *module, PyObject *new_limit)
1672-
/*[clinic end generated code: output=f2799ecd908e250b input=cec70e9226406435]*/
1666+
/*[clinic end generated code: output=f2799ecd908e250b input=77db7485ee3ae90a]*/
16731667
{
16741668
_csvstate *module_state = get_csv_state(module);
16751669
Py_ssize_t old_limit = FT_ATOMIC_LOAD_SSIZE_RELAXED(module_state->field_limit);
@@ -1705,37 +1699,38 @@ PyType_Spec error_spec = {
17051699
PyDoc_STRVAR(csv_module_doc, "CSV parsing and writing.\n");
17061700

17071701
PyDoc_STRVAR(csv_reader_doc,
1708-
" csv_reader = reader(iterable [, dialect='excel']\n"
1709-
" [optional keyword args])\n"
1710-
" for row in csv_reader:\n"
1711-
" process(row)\n"
1702+
"reader($module, iterable, /, dialect='excel', **fmtparams)\n"
1703+
"--\n\n"
1704+
"Return a reader object that will process lines from the given iterable.\n"
17121705
"\n"
17131706
"The \"iterable\" argument can be any object that returns a line\n"
17141707
"of input for each iteration, such as a file object or a list. The\n"
1715-
"optional \"dialect\" parameter is discussed below. The function\n"
1708+
"optional \"dialect\" argument defines a CSV dialect. The function\n"
17161709
"also accepts optional keyword arguments which override settings\n"
17171710
"provided by the dialect.\n"
17181711
"\n"
17191712
"The returned object is an iterator. Each iteration returns a row\n"
17201713
"of the CSV file (which can span multiple input lines).\n");
17211714

17221715
PyDoc_STRVAR(csv_writer_doc,
1723-
" csv_writer = csv.writer(fileobj [, dialect='excel']\n"
1724-
" [optional keyword args])\n"
1725-
" for row in sequence:\n"
1726-
" csv_writer.writerow(row)\n"
1716+
"writer($module, fileobj, /, dialect='excel', **fmtparams)\n"
1717+
"--\n\n"
1718+
"Return a writer object that will write user data on the given file object.\n"
17271719
"\n"
1728-
" [or]\n"
1729-
"\n"
1730-
" csv_writer = csv.writer(fileobj [, dialect='excel']\n"
1731-
" [optional keyword args])\n"
1732-
" csv_writer.writerows(rows)\n"
1733-
"\n"
1734-
"The \"fileobj\" argument can be any object that supports the file API.\n");
1720+
"The \"fileobj\" argument can be any object that supports the file API.\n"
1721+
"The optional \"dialect\" argument defines a CSV dialect. The function\n"
1722+
"also accepts optional keyword arguments which override settings\n"
1723+
"provided by the dialect.\n");
17351724

17361725
PyDoc_STRVAR(csv_register_dialect_doc,
1737-
"Create a mapping from a string name to a dialect class.\n"
1738-
" dialect = csv.register_dialect(name[, dialect[, **fmtparams]])");
1726+
"register_dialect($module, name, /, dialect='excel', **fmtparams)\n"
1727+
"--\n\n"
1728+
"Create a mapping from a string name to a CVS dialect.\n"
1729+
"\n"
1730+
"The optional \"dialect\" argument specifies the base dialect instance\n"
1731+
"or the name of the registered dialect. The function also accepts\n"
1732+
"optional keyword arguments which override settings provided by the\n"
1733+
"dialect.\n");
17391734

17401735
static struct PyMethodDef csv_methods[] = {
17411736
{ "reader", _PyCFunction_CAST(csv_reader),

Modules/clinic/_csv.c.h

Lines changed: 4 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)