Skip to content

Commit a3c45b6

Browse files
author
Erlend E. Aasland
committed
Merge branch 'main' into sqlite-expose-error-code
2 parents 3589a6a + 24da544 commit a3c45b6

File tree

19 files changed

+556
-262
lines changed

19 files changed

+556
-262
lines changed

Include/internal/pycore_frame.h

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ typedef struct _interpreter_frame {
2929
PyObject *generator;
3030
struct _interpreter_frame *previous;
3131
int f_lasti; /* Last instruction if called */
32-
int stackdepth; /* Depth of value stack */
33-
int nlocalsplus;
34-
PyFrameState f_state; /* What state the frame is in */
35-
PyObject *stack[1];
32+
int stacktop; /* Offset of TOS from localsplus */
33+
PyFrameState f_state; /* What state the frame is in */
34+
PyObject *localsplus[1];
3635
} InterpreterFrame;
3736

3837
static inline int _PyFrame_IsRunnable(InterpreterFrame *f) {
@@ -47,6 +46,26 @@ static inline int _PyFrameHasCompleted(InterpreterFrame *f) {
4746
return f->f_state > FRAME_EXECUTING;
4847
}
4948

49+
static inline PyObject **_PyFrame_Stackbase(InterpreterFrame *f) {
50+
return f->localsplus + f->f_code->co_nlocalsplus;
51+
}
52+
53+
static inline PyObject *_PyFrame_StackPeek(InterpreterFrame *f) {
54+
assert(f->stacktop > f->f_code->co_nlocalsplus);
55+
return f->localsplus[f->stacktop-1];
56+
}
57+
58+
static inline PyObject *_PyFrame_StackPop(InterpreterFrame *f) {
59+
assert(f->stacktop > f->f_code->co_nlocalsplus);
60+
f->stacktop--;
61+
return f->localsplus[f->stacktop];
62+
}
63+
64+
static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) {
65+
f->localsplus[f->stacktop] = value;
66+
f->stacktop++;
67+
}
68+
5069
#define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *))
5170

5271
InterpreterFrame *
@@ -61,8 +80,7 @@ _PyFrame_InitializeSpecials(
6180
frame->f_builtins = Py_NewRef(con->fc_builtins);
6281
frame->f_globals = Py_NewRef(con->fc_globals);
6382
frame->f_locals = Py_XNewRef(locals);
64-
frame->nlocalsplus = nlocalsplus;
65-
frame->stackdepth = 0;
83+
frame->stacktop = nlocalsplus;
6684
frame->frame_obj = NULL;
6785
frame->generator = NULL;
6886
frame->f_lasti = -1;
@@ -75,7 +93,19 @@ _PyFrame_InitializeSpecials(
7593
static inline PyObject**
7694
_PyFrame_GetLocalsArray(InterpreterFrame *frame)
7795
{
78-
return ((PyObject **)frame) - frame->nlocalsplus;
96+
return frame->localsplus;
97+
}
98+
99+
static inline PyObject**
100+
_PyFrame_GetStackPointer(InterpreterFrame *frame)
101+
{
102+
return frame->localsplus+frame->stacktop;
103+
}
104+
105+
static inline void
106+
_PyFrame_SetStackPointer(InterpreterFrame *frame, PyObject **stack_pointer)
107+
{
108+
frame->stacktop = (int)(stack_pointer - frame->localsplus);
79109
}
80110

81111
/* For use by _PyFrame_GetFrameObject

Lib/enum.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,17 +1390,28 @@ def _power_of_two(value):
13901390
return value == 2 ** _high_bit(value)
13911391

13921392
def global_enum_repr(self):
1393-
return '%s.%s' % (self.__class__.__module__, self._name_)
1393+
"""
1394+
use module.enum_name instead of class.enum_name
1395+
1396+
the module is the last module in case of a multi-module name
1397+
"""
1398+
module = self.__class__.__module__.split('.')[-1]
1399+
return '%s.%s' % (module, self._name_)
13941400

13951401
def global_flag_repr(self):
1396-
module = self.__class__.__module__
1402+
"""
1403+
use module.flag_name instead of class.flag_name
1404+
1405+
the module is the last module in case of a multi-module name
1406+
"""
1407+
module = self.__class__.__module__.split('.')[-1]
13971408
cls_name = self.__class__.__name__
13981409
if self._name_ is None:
1399-
return "%x" % (module, cls_name, self._value_)
1410+
return "%s.%s(0x%x)" % (module, cls_name, self._value_)
14001411
if _is_single_bit(self):
14011412
return '%s.%s' % (module, self._name_)
14021413
if self._boundary_ is not FlagBoundary.KEEP:
1403-
return module + module.join(self.name.split('|'))
1414+
return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')])
14041415
else:
14051416
name = []
14061417
for n in self._name_.split('|'):

Lib/sqlite3/test/dbapi.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,17 @@
2222

2323
import contextlib
2424
import sqlite3 as sqlite
25+
import subprocess
2526
import sys
2627
import threading
2728
import unittest
2829

29-
from test.support import check_disallow_instantiation, threading_helper, bigmemtest
30+
from test.support import (
31+
SHORT_TIMEOUT,
32+
bigmemtest,
33+
check_disallow_instantiation,
34+
threading_helper,
35+
)
3036
from test.support.os_helper import TESTFN, unlink, temp_dir
3137

3238

@@ -1069,6 +1075,77 @@ def test_on_conflict_replace(self):
10691075
self.assertEqual(self.cu.fetchall(), [('Very different data!', 'foo')])
10701076

10711077

1078+
class MultiprocessTests(unittest.TestCase):
1079+
CONNECTION_TIMEOUT = SHORT_TIMEOUT / 1000. # Defaults to 30 ms
1080+
1081+
def tearDown(self):
1082+
unlink(TESTFN)
1083+
1084+
def test_ctx_mgr_rollback_if_commit_failed(self):
1085+
# bpo-27334: ctx manager does not rollback if commit fails
1086+
SCRIPT = f"""if 1:
1087+
import sqlite3
1088+
def wait():
1089+
print("started")
1090+
assert "database is locked" in input()
1091+
1092+
cx = sqlite3.connect("{TESTFN}", timeout={self.CONNECTION_TIMEOUT})
1093+
cx.create_function("wait", 0, wait)
1094+
with cx:
1095+
cx.execute("create table t(t)")
1096+
try:
1097+
# execute two transactions; both will try to lock the db
1098+
cx.executescript('''
1099+
-- start a transaction and wait for parent
1100+
begin transaction;
1101+
select * from t;
1102+
select wait();
1103+
rollback;
1104+
1105+
-- start a new transaction; would fail if parent holds lock
1106+
begin transaction;
1107+
select * from t;
1108+
rollback;
1109+
''')
1110+
finally:
1111+
cx.close()
1112+
"""
1113+
1114+
# spawn child process
1115+
proc = subprocess.Popen(
1116+
[sys.executable, "-c", SCRIPT],
1117+
encoding="utf-8",
1118+
bufsize=0,
1119+
stdin=subprocess.PIPE,
1120+
stdout=subprocess.PIPE,
1121+
)
1122+
self.addCleanup(proc.communicate)
1123+
1124+
# wait for child process to start
1125+
self.assertEqual("started", proc.stdout.readline().strip())
1126+
1127+
cx = sqlite.connect(TESTFN, timeout=self.CONNECTION_TIMEOUT)
1128+
try: # context manager should correctly release the db lock
1129+
with cx:
1130+
cx.execute("insert into t values('test')")
1131+
except sqlite.OperationalError as exc:
1132+
proc.stdin.write(str(exc))
1133+
else:
1134+
proc.stdin.write("no error")
1135+
finally:
1136+
cx.close()
1137+
1138+
# terminate child process
1139+
self.assertIsNone(proc.returncode)
1140+
try:
1141+
proc.communicate(input="end", timeout=SHORT_TIMEOUT)
1142+
except subprocess.TimeoutExpired:
1143+
proc.kill()
1144+
proc.communicate()
1145+
raise
1146+
self.assertEqual(proc.returncode, 0)
1147+
1148+
10721149
def suite():
10731150
tests = [
10741151
ClosedConTests,
@@ -1078,6 +1155,7 @@ def suite():
10781155
CursorTests,
10791156
ExtensionTests,
10801157
ModuleTests,
1158+
MultiprocessTests,
10811159
OpenTests,
10821160
SqliteOnConflictTests,
10831161
ThreadTests,

Lib/test/test_enum.py

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def load_tests(loader, tests, ignore):
2828
))
2929
return tests
3030

31+
MODULE = ('test.test_enum', '__main__')[__name__=='__main__']
32+
SHORT_MODULE = MODULE.split('.')[-1]
33+
3134
# for pickle tests
3235
try:
3336
class Stooges(Enum):
@@ -143,6 +146,23 @@ def __init__(self, fget=None, fset=None, fdel=None, doc=None):
143146
def __get__(self, instance, ownerclass):
144147
return self.fget(ownerclass)
145148

149+
# for global repr tests
150+
151+
@enum.global_enum
152+
class HeadlightsK(IntFlag, boundary=enum.KEEP):
153+
OFF_K = 0
154+
LOW_BEAM_K = auto()
155+
HIGH_BEAM_K = auto()
156+
FOG_K = auto()
157+
158+
159+
@enum.global_enum
160+
class HeadlightsC(IntFlag, boundary=enum.CONFORM):
161+
OFF_C = 0
162+
LOW_BEAM_C = auto()
163+
HIGH_BEAM_C = auto()
164+
FOG_C = auto()
165+
146166

147167
# tests
148168

@@ -3224,6 +3244,34 @@ def test_repr(self):
32243244
self.assertEqual(repr(~(Open.WO | Open.CE)), 'Open.RW')
32253245
self.assertEqual(repr(Open(~4)), '-5')
32263246

3247+
def test_global_repr_keep(self):
3248+
self.assertEqual(
3249+
repr(HeadlightsK(0)),
3250+
'%s.OFF_K' % SHORT_MODULE,
3251+
)
3252+
self.assertEqual(
3253+
repr(HeadlightsK(2**0 + 2**2 + 2**3)),
3254+
'%(m)s.LOW_BEAM_K|%(m)s.FOG_K|0x8' % {'m': SHORT_MODULE},
3255+
)
3256+
self.assertEqual(
3257+
repr(HeadlightsK(2**3)),
3258+
'%(m)s.HeadlightsK(0x8)' % {'m': SHORT_MODULE},
3259+
)
3260+
3261+
def test_global_repr_conform1(self):
3262+
self.assertEqual(
3263+
repr(HeadlightsC(0)),
3264+
'%s.OFF_C' % SHORT_MODULE,
3265+
)
3266+
self.assertEqual(
3267+
repr(HeadlightsC(2**0 + 2**2 + 2**3)),
3268+
'%(m)s.LOW_BEAM_C|%(m)s.FOG_C' % {'m': SHORT_MODULE},
3269+
)
3270+
self.assertEqual(
3271+
repr(HeadlightsC(2**3)),
3272+
'%(m)s.OFF_C' % {'m': SHORT_MODULE},
3273+
)
3274+
32273275
def test_format(self):
32283276
Perm = self.Perm
32293277
self.assertEqual(format(Perm.R, ''), '4')
@@ -4085,7 +4133,7 @@ def setUp(self):
40854133
def test_convert_value_lookup_priority(self):
40864134
test_type = enum.IntEnum._convert_(
40874135
'UnittestConvert',
4088-
('test.test_enum', '__main__')[__name__=='__main__'],
4136+
MODULE,
40894137
filter=lambda x: x.startswith('CONVERT_TEST_'))
40904138
# We don't want the reverse lookup value to vary when there are
40914139
# multiple possible names for a given value. It should always
@@ -4095,7 +4143,7 @@ def test_convert_value_lookup_priority(self):
40954143
def test_convert(self):
40964144
test_type = enum.IntEnum._convert_(
40974145
'UnittestConvert',
4098-
('test.test_enum', '__main__')[__name__=='__main__'],
4146+
MODULE,
40994147
filter=lambda x: x.startswith('CONVERT_TEST_'))
41004148
# Ensure that test_type has all of the desired names and values.
41014149
self.assertEqual(test_type.CONVERT_TEST_NAME_F,
@@ -4115,7 +4163,7 @@ def test_convert_warn(self):
41154163
with self.assertWarns(DeprecationWarning):
41164164
enum.IntEnum._convert(
41174165
'UnittestConvert',
4118-
('test.test_enum', '__main__')[__name__=='__main__'],
4166+
MODULE,
41194167
filter=lambda x: x.startswith('CONVERT_TEST_'))
41204168

41214169
@unittest.skipUnless(python_version >= (3, 9),
@@ -4124,16 +4172,15 @@ def test_convert_raise(self):
41244172
with self.assertRaises(AttributeError):
41254173
enum.IntEnum._convert(
41264174
'UnittestConvert',
4127-
('test.test_enum', '__main__')[__name__=='__main__'],
4175+
MODULE,
41284176
filter=lambda x: x.startswith('CONVERT_TEST_'))
41294177

41304178
def test_convert_repr_and_str(self):
4131-
module = ('test.test_enum', '__main__')[__name__=='__main__']
41324179
test_type = enum.IntEnum._convert_(
41334180
'UnittestConvert',
4134-
module,
4181+
MODULE,
41354182
filter=lambda x: x.startswith('CONVERT_STRING_TEST_'))
4136-
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % module)
4183+
self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE)
41374184
self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), 'CONVERT_STRING_TEST_NAME_A')
41384185
self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5')
41394186

@@ -4151,7 +4198,7 @@ def setUp(self):
41514198
def test_convert(self):
41524199
test_type = enum.StrEnum._convert_(
41534200
'UnittestConvert',
4154-
('test.test_enum', '__main__')[__name__=='__main__'],
4201+
MODULE,
41554202
filter=lambda x: x.startswith('CONVERT_STR_'))
41564203
# Ensure that test_type has all of the desired names and values.
41574204
self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello')
@@ -4162,12 +4209,11 @@ def test_convert(self):
41624209
[], msg='Names other than CONVERT_STR_* found.')
41634210

41644211
def test_convert_repr_and_str(self):
4165-
module = ('test.test_enum', '__main__')[__name__=='__main__']
41664212
test_type = enum.StrEnum._convert_(
41674213
'UnittestConvert',
4168-
module,
4214+
MODULE,
41694215
filter=lambda x: x.startswith('CONVERT_STR_'))
4170-
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % module)
4216+
self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE)
41714217
self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye')
41724218
self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello')
41734219

Lib/test/test_marshal.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,31 @@ def test_eof(self):
344344
for i in range(len(data)):
345345
self.assertRaises(EOFError, marshal.loads, data[0: i])
346346

347+
def test_deterministic_sets(self):
348+
# bpo-37596: To support reproducible builds, sets and frozensets need to
349+
# have their elements serialized in a consistent order (even when they
350+
# have been scrambled by hash randomization):
351+
for kind in ("set", "frozenset"):
352+
for elements in (
353+
"float('nan'), b'a', b'b', b'c', 'x', 'y', 'z'",
354+
# Also test for bad interactions with backreferencing:
355+
"('string', 1), ('string', 2), ('string', 3)",
356+
):
357+
s = f"{kind}([{elements}])"
358+
with self.subTest(s):
359+
# First, make sure that our test case still has different
360+
# orders under hash seeds 0 and 1. If this check fails, we
361+
# need to update this test with different elements:
362+
args = ["-c", f"print({s})"]
363+
_, repr_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0")
364+
_, repr_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1")
365+
self.assertNotEqual(repr_0, repr_1)
366+
# Then, perform the actual test:
367+
args = ["-c", f"import marshal; print(marshal.dumps({s}))"]
368+
_, dump_0, _ = assert_python_ok(*args, PYTHONHASHSEED="0")
369+
_, dump_1, _ = assert_python_ok(*args, PYTHONHASHSEED="1")
370+
self.assertEqual(dump_0, dump_1)
371+
347372
LARGE_SIZE = 2**31
348373
pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4
349374

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix some edge cases of ``enum.Flag`` string representation in the REPL.
2+
Patch by Pablo Galindo.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
The :mod:`sqlite3` context manager now performs a rollback (thus releasing the
2+
database lock) if commit failed. Patch by Luca Citi and Erlend E. Aasland.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Ensure that :class:`set` and :class:`frozenset` objects are always
2+
:mod:`marshalled <marshal>` reproducibly.

0 commit comments

Comments
 (0)