8000 Merge branch 'main' of https://github.com/python/cpython · python/cpython@be8ae08 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit be8ae08

Browse files
committed
Merge branch 'main' of https://github.com/python/cpython
2 parents d70e5c1 + 7303f06 commit be8ae08

File tree

12 files changed

+175
-68
lines changed

12 files changed

+175
-68
lines changed

Include/internal/pycore_pyerrors.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ PyAPI_FUNC(void) _PyErr_SetString(
130130
PyObject *exception,
131131
const char *string);
132132

133+
/*
134+
* Set an exception with the error message decoded from the current locale
135+
* encoding (LC_CTYPE).
136+
*
137+
* Exceptions occurring in decoding take priority over the desired exception.
138+
*
139+
* Exported for '_ctypes' shared extensions.
140+
*/
141+
PyAPI_FUNC(void) _PyErr_SetLocaleString(
142+
PyObject *exception,
143+
const char *string);
144+
133145
PyAPI_FUNC(PyObject*) _PyErr_Format(
134146
PyThreadState *tstate,
135147
PyObject *exception,

Lib/test/test_ctypes/test_dlerror.py

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import _ctypes
12
import os
3+
import platform
24
import sys
5+
import test.support
36
import unittest
4-
import platform
7+
from ctypes import CDLL, c_int
8+
from ctypes.util import find_library
9+
510

611
FOO_C = r"""
712
#include <unistd.h>
@@ -26,7 +31,7 @@
2631

2732

2833
@unittest.skipUnless(sys.platform.startswith('linux'),
29-
'Test only valid for Linux')
34+
'test requires GNU IFUNC support')
3035
class TestNullDlsym(unittest.TestCase):
3136
"""GH-126554: Ensure that we catch NULL dlsym return values
3237
@@ -53,14 +58,6 @@ def test_null_dlsym(self):
5358
import subprocess
5459
import tempfile
5560

56-
# To avoid ImportErrors on Windows, where _ctypes does not have
57-
# dlopen and dlsym,
58-
# import here, i.e., inside the test function.
59-
# The skipUnless('linux') decorator ensures that we're on linux
60-
# if we're executing these statements.
61-
from ctypes import CDLL, c_int
62-
from _ctypes import dlopen, dlsym
63-
6461
retcode = subprocess.call(["gcc", "--version"],
6562
stdout=subprocess.DEVNULL,
6663
stderr=subprocess.DEVNULL)
@@ -111,6 +108,8 @@ def test_null_dlsym(self):
111108
self.assertEqual(os.read(pipe_r, 2), b'OK')
112109

113110
# Case #3: Test 'py_dl_sym' from Modules/_ctypes/callproc.c
111+
dlopen = test.support.get_attribute(_ctypes, 'dlopen')
112+
dlsym = test.support.get_attribute(_ctypes, 'dlsym')
114113
L = dlopen(dstname)
115114
with self.assertRaisesRegex(OSError, "symbol 'foo' not found"):
116115
dlsym(L, "foo")
@@ -119,5 +118,66 @@ def test_null_dlsym(self):
119118
self.assertEqual(os.read(pipe_r, 2), b'OK')
120119

121120

121+
@unittest.skipUnless(os.name != 'nt', 'test requires dlerror() calls')
122+
class TestLocalization(unittest.TestCase):
123+
124+
@staticmethod
125+
def configure_locales(func):
126+
return test.support.run_with_locale(
127+
'LC_ALL',
128+
'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk',
129+
'fr_FR.utf8', 'en_US.utf8',
130+
'',
131+
)(func)
132+
133+
@classmethod
134+
def setUpClass(cls):
135+
cls.libc_filename = find_library("c")
136+
137+
@configure_locales
138+
def test_localized_error_from_dll(self):
139+
dll = CDLL(self.libc_filename)
140+
with self.assertRaises(AttributeError) as cm:
141+
dll.this_name_does_not_exist
142+
if sys.platform.startswith('linux'):
143+
# On macOS, the filename is not reported by dlerror().
144+
self.assertIn(self.libc_filename, str(cm.exception))
145+
146+
@configure_locales
147+
def test_localized_error_in_dll(self):
148+
dll = CDLL(self.libc_filename)
149+
with self.assertRaises(ValueError) as cm:
150+
c_int.in_dll(dll, 'this_name_does_not_exist')
151+
if sys.platform.startswith('linux'):
152+
# On macOS, the filename is not reported by dlerror().
153+
self.assertIn(self.libc_filename, str(cm.exception))
154+
155+
@unittest.skipUnless(hasattr(_ctypes, 'dlopen'),
156+
'test requires _ctypes.dlopen()')
157+
@configure_locales
158+
def test_localized_error_dlopen(self):
159+
missing_filename = b'missing\xff.so'
160+
# Depending whether the locale, we may encode '\xff' differently
161+
# but we are only interested in avoiding a UnicodeDecodeError
162+
# when reporting the dlerror() error message which contains
163+
# the localized filename.
164+
filename_pattern = r'missing.*?\.so'
165+
with self.assertRaisesRegex(OSError, filename_pattern):
166+
_ctypes.dlopen(missing_filename, 2)
167+
168+
@unittest.skipUnless(hasattr(_ctypes, 'dlopen'),
169+
'test requires _ctypes.dlopen()')
170+
@unittest.skipUnless(hasattr(_ctypes, 'dlsym'),
171+
'test requires _ctypes.dlsym()')
172+
@configure_locales
173+
def test_localized_error_dlsym(self):
174+
dll = _ctypes.dlopen(self.libc_filename)
175+
with self.assertRaises(OSError) as cm:
176+
_ctypes.dlsym(dll, 'this_name_does_not_exist')
177+
if sys.platform.startswith('linux'):
178+
# On macOS, the filename is not reported by dlerror().
179+
self.assertIn(self.libc_filename, str(cm.exception))
180+
181+
122182
if __name__ == "__main__":
123183
unittest.main()

Lib/test/test_dbm_gnu.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from test import support
2-
from test.support import import_helper, cpython_only
3-
gdbm = import_helper.import_module("dbm.gnu") #skip if not supported
4-
import unittest
51
import os
6-
from test.support.os_helper import TESTFN, TESTFN_NONASCII, unlink, FakePath
2+
import unittest
3+
from test import support
4+
from test.support import cpython_only, import_helper
5+
from test.support.os_helper import (TESTFN, TESTFN_NONASCII, FakePath,
6+
create_empty_file, temp_dir, unlink)
77

8+
gdbm = import_helper.import_module("dbm.gnu") # skip if not supported
89

910
filename = TESTFN
1011

@@ -205,6 +206,16 @@ def test_clear(self):
205206
self.assertNotIn(k, db)
206207
self.assertEqual(len(db), 0)
207208

209+
@support.run_with_locale(
210+
'LC_ALL',
211+
'fr_FR.iso88591', 'ja_JP.sjis', 'zh_CN.gbk',
212+
'fr_FR.utf8', 'en_US.utf8',
213+
'',
214+
)
215+
def test_localized_error(self):
216+
with temp_dir() as d:
217+
create_empty_file(os.path.join(d, 'test'))
218+
self.assertRaises(gdbm.error, gdbm.open, filename, 'r')
208219

209220

210221
if __name__ == '__main__':

Modules/_ctypes/_ctypes.c

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -984,15 +984,8 @@ CDataType_in_dll_impl(PyObject *type, PyTypeObject *cls, PyObject *dll,
984984
#ifdef USE_DLERROR
985985
const char *dlerr = dlerror();
986986
if (dlerr) {
987-
PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape");
988-
if (message) {
989-
PyErr_SetObject(PyExc_ValueError, message);
990-
Py_DECREF(message);
991-
return NULL;
992-
}
993-
// Ignore errors from PyUnicode_DecodeLocale,
994-
// fall back to the generic error below.
995-
PyErr_Clear();
987+
_PyErr_SetLocaleString(PyExc_ValueError, dlerr);
988+
return NULL;
996989
}
997990
#endif
998991
#undef USE_DLERROR
@@ -3825,21 +3818,14 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
38253818
address = (PPROC)dlsym(handle, name);
38263819

38273820
if (!address) {
3828-
#ifdef USE_DLERROR
3821+
#ifdef USE_DLERROR
3829 F438 3822
const char *dlerr = dlerror();
38303823
if (dlerr) {
3831-
PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape");
3832-
if (message) {
3833-
PyErr_SetObject(PyExc_AttributeError, message);
3834-
Py_DECREF(ftuple);
3835-
Py_DECREF(message);
3836-
return NULL;
3837-
}
3838-
// Ignore errors from PyUnicode_DecodeLocale,
3839-
// fall back to the generic error below.
3840-
PyErr_Clear();
3824+
_PyErr_SetLocaleString(PyExc_AttributeError, dlerr);
3825+
Py_DECREF(ftuple);
3826+
return NULL;
38413827
}
3842-
#endif
3828+
#endif
38433829
PyErr_Format(PyExc_AttributeError, "function '%s' not found", name);
38443830
Py_DECREF(ftuple);
38453831
return NULL;

Modules/_ctypes/callproc.c

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,10 +1588,11 @@ static PyObject *py_dl_open(PyObject *self, PyObject *args)
15881588
Py_XDECREF(name2);
15891589
if (!handle) {
15901590
const char *errmsg = dlerror();
1591-
if (!errmsg)
1592-
errmsg = "dlopen() error";
1593-
PyErr_SetString(PyExc_OSError,
1594-
errmsg);
1591+
if (errmsg) {
1592+
_PyErr_SetLocaleString(PyExc_OSError, errmsg);
1593+
return NULL;
1594+
}
1595+
PyErr_SetString(PyExc_OSError, "dlopen() error");
15951596
return NULL;
15961597
}
15971598
return PyLong_FromVoidPtr(handle);
@@ -1604,8 +1605,12 @@ static PyObject *py_dl_close(PyObject *self, PyObject *args)
16041605
if (!PyArg_ParseTuple(args, "O&:dlclose", &_parse_voidp, &handle))
16051606
return NULL;
16061607
if (dlclose(handle)) {
1607-
PyErr_SetString(PyExc_OSError,
1608-
dlerror());
1608+
const char *errmsg = dlerror();
1609+
if (errmsg) {
1610+
_PyErr_SetLocaleString(PyExc_OSError, errmsg);
1611+
return NULL;
1612+
}
1613+
PyErr_SetString(PyExc_OSError, "dlclose() error");
16091614
return NULL;
16101615
}
16111616
Py_RETURN_NONE;
@@ -1639,21 +1644,14 @@ static PyObject *py_dl_sym(PyObject *self, PyObject *args)
16391644
if (ptr) {
16401645
return PyLong_FromVoidPtr(ptr);
16411646
}
1642-
#ifdef USE_DLERROR
1643-
const char *dlerr = dlerror();
1644-
if (dlerr) {
1645-
PyObject *message = PyUnicode_DecodeLocale(dlerr, "surrogateescape");
1646-
if (message) {
1647-
PyErr_SetObject(PyExc_OSError, message);
1648-
Py_DECREF(message);
1649-
return NULL;
1650-
}
1651-
// Ignore errors from PyUnicode_DecodeLocale,
1652-
// fall back to the generic error below.
1653-
PyErr_Clear();
1647+
#ifdef USE_DLERROR
1648+
const char *errmsg = dlerror();
1649+
if (errmsg) {
1650+
_PyErr_SetLocaleString(PyExc_OSError, errmsg);
1651+
return NULL;
16541652
}
1655-
#endif
1656-
#undef USE_DLERROR
1653+
#endif
1654+
#undef USE_DLERROR
16571655
PyErr_Format(PyExc_OSError, "symbol '%s' not found", name);
16581656
return NULL;
16591657
}

Modules/_gdbmmodule.c

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
#endif
99

1010
#include "Python.h"
11+
#include "pycore_pyerrors.h" // _PyErr_SetLocaleString()
1112
#include "gdbm.h"
1213

1314
#include <fcntl.h>
14-
#include <stdlib.h> // free()
15+
#include <stdlib.h> // free()
1516
#include <sys/stat.h>
1617
#include <sys/types.h>
1718

@@ -33,6 +34,24 @@ get_gdbm_state(PyObject *module)
3334
return (_gdbm_state *)state;
3435
}
3536

37+
/*
38+
* Set the gdbm error obtained by gdbm_strerror(gdbm_errno).
39+
*
40+
* If no error message exists, a generic (UTF-8) error message
41+
* is used instead.
42+
*/
43+
static void
44+
set_gdbm_error(_gdbm_state *state, const char *generic_error)
45+
{
46+
const char *gdbm_errmsg = gdbm_strerror(gdbm_errno);
47+
if (gdbm_errmsg) {
48+
_PyErr_SetLocaleString(state->gdbm_error, gdbm_errmsg);
49+
}
50+
else {
51+
PyErr_SetString(state->gdbm_error, generic_error);
52+
}
53+
}
54+
3655
/*[clinic input]
3756
module _gdbm
3857
class _gdbm.gdbm "gdbmobject *" "&Gdbmtype"
@@ -91,7 +110,7 @@ newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode)
91110
PyErr_SetFromErrnoWithFilename(state->gdbm_error, file);
92111
}
93112
else {
94-
PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
113+
set_gdbm_error(state, "gdbm_open() error");
95114
}
96115
Py_DECREF(dp);
97116
return NULL;
@@ -136,7 +155,7 @@ gdbm_length(gdbmobject *dp)
136155
PyErr_SetFromErrno(state->gdbm_error);
137156
}
138157
else {
139-
PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
158+
set_gdbm_error(state, "gdbm_count() error");
140159
}
141160
return -1;
142161
}
@@ -286,7 +305,7 @@ gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w)
286305
PyErr_SetObject(PyExc_KeyError, v);
287306
}
288307
else {
289-
PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
308+
set_gdbm_error(state, "gdbm_delete() error");
290309
}
291310
return -1;
292311
}
@@ -297,11 +316,12 @@ gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w)
297316
}
298317
errno = 0;
299318
if (gdbm_store(dp->di_dbm, krec, drec, GDBM_REPLACE) < 0) {
300-
if (errno != 0)
319+
if (errno != 0) {
301320
PyErr_SetFromErrno(state->gdbm_error);
302-
else
303-
PyErr_SetString(state->gdbm_error,
304-
gdbm_strerror(gdbm_errno));
321+
}
322+
else {
323+
set_gdbm_error(state, "gdbm_store() error");
324+
}
305325
return -1;
306326
}
307327
}
@@ -534,10 +554,12 @@ _gdbm_gdbm_reorganize_impl(gdbmobject *self, PyTypeObject *cls)
534554
check_gdbmobject_open(self, state->gdbm_error);
535555
errno = 0;
536556
if (gdbm_reorganize(self->di_dbm) < 0) {
537-
if (errno != 0)
557+
if (errno != 0) {
538558
PyErr_SetFromErrno(state->gdbm_error);
539-
else
540-
PyErr_SetString(state->gdbm_error, gdbm_strerror(gdbm_errno));
559+
}
560+
else {
561+
set_gdbm_error(state, "gdbm_reorganize() error");
562+
}
541563
return NULL;
542564
}
543565
Py_RETURN_NONE;

Modules/_hashopenssl.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ _setException(PyObject *exc, const char* altmsg, ...)
319319
va_end(vargs);
320320
ERR_clear_error();
321321

322+
/* ERR_ERROR_STRING(3) ensures that the messages below are ASCII */
322323
lib = ERR_lib_error_string(errcode);
323324
func = ERR_func_error_string(errcode);
324325
reason = ERR_reason_error_string(errcode);

Modules/_sqlite/util.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ _pysqlite_seterror(pysqlite_state *state, sqlite3 *db)
134134

135135
/* Create and set the exception. */
136136
int extended_errcode = sqlite3_extended_errcode(db);
137+
// sqlite3_errmsg() always returns an UTF-8 encoded message
137138
const char *errmsg = sqlite3_errmsg(db);
138139
raise_exception(exc_class, extended_errcode, errmsg);
139140
return extended_errcode;

Modules/_testcapi/exceptions.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "parts.h"
55
#include "util.h"
6+
67
#include "clinic/exceptions.c.h"
78

89

0 commit comments

Comments
 (0)
0