Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2616,7 +2616,9 @@ expression support in the :mod:`re` module).
:func:`re.split`). Splitting an empty string with a specified separator
returns ``['']``.

For example::
For example:

.. doctest::

>>> '1,2,3'.split(',')
['1', '2', '3']
Expand All @@ -2634,7 +2636,9 @@ expression support in the :mod:`re` module).
string or a string consisting of just whitespace with a ``None`` separator
returns ``[]``.

For example::
For example:

.. doctest::

>>> '1 2 3'.split()
['1', '2', '3']
Expand All @@ -2646,7 +2650,9 @@ expression support in the :mod:`re` module).
If *sep* is not specified or is ``None`` and *maxsplit* is ``0``, only
leading runs of consecutive whitespace are considered.

For example::
For example:

.. doctest::

>>> "".split(None, 0)
[]
Expand All @@ -2655,7 +2661,7 @@ expression support in the :mod:`re` module).
>>> " foo ".split(maxsplit=0)
['foo ']

See also :meth:`join`.
See also :meth:`join` and :meth:`rsplit`.


.. index::
Expand Down
9 changes: 8 additions & 1 deletion Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1373,9 +1373,16 @@ def getLogger(self, name):
logger and fix up the parent/child references which pointed to the
placeholder to now point to the logger.
"""
rv = None
if not isinstance(name, str):
raise TypeError('A logger name must be a string')
# Fast path: an already-registered, non-placeholder logger can be
# returned without taking the lock. dict.get() is atomic under both
# the GIL and free threading, and a Logger is fully initialised before
# being inserted into loggerDict under the lock, so this never sees a
# partially-constructed object.
rv = self.loggerDict.get(name)
if rv is not None and not isinstance(rv, PlaceHolder):
return rv
with _lock:
if name in self.loggerDict:
rv = self.loggerDict[name]
Expand Down
11 changes: 10 additions & 1 deletion Lib/logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,16 @@ def __init__(self, filename, when='h', interval=1, backupCount=0,
# path object (see Issue #27493), but self.baseFilename will be a string
filename = self.baseFilename
if os.path.exists(filename):
t = int(os.stat(filename).st_mtime)
# Use the minimum of file creation and modification time as
# the base of the rollover calculation
stat_result = os.stat(filename)
# Use st_birthtime whenever it is available or use st_ctime
# instead otherwise
try:
creation_time = stat_result.st_birthtime
except AttributeError:
creation_time = stat_result.st_ctime
t = int(min(creation_time, stat_result.st_mtime))
else:
t = int(time.time())
self.rolloverAt = self.computeRollover(t)
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"has_fork_support", "requires_fork",
"has_subprocess_support", "requires_subprocess",
"has_socket_support", "requires_working_socket",
"has_st_birthtime",
"has_remote_subprocess_debugging", "requires_remote_subprocess_debugging",
"anticipate_failure", "load_package_tests", "detect_api_mismatch",
"check__all__", "skip_if_buggy_ucrt_strfptime",
Expand Down Expand Up @@ -620,6 +621,10 @@ def skip_wasi_stack_overflow():
or is_android
)

# At the moment, st_birthtime attribute is only supported on Windows,
# MacOS and FreeBSD.
has_st_birthtime = sys.platform.startswith(("win", "freebsd", "darwin"))

def requires_fork():
return unittest.skipUnless(has_fork_support, "requires working os.fork()")

Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_deque.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,22 @@ def test_index(self):
else:
self.assertEqual(d.index(element, start, stop), target)

# Test stop argument
for elem in d:
index = d.index(elem)
self.assertEqual(
index,
d.index(elem, 0),
)
self.assertEqual(
index,
d.index(elem, 0, len(d)),
)
self.assertEqual(
index,
d.index(elem, 0, len(d) + 100),
)

# Test large start argument
d = deque(range(0, 10000, 10))
for step in range(100):
Expand Down
24 changes: 24 additions & 0 deletions Lib/test/test_free_threading/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ def copy_loop():

threading_helper.run_concurrently([mutate, copy_loop])

def test_index_race_in_ac(self):
# gh-150750: There was a c_default specified as `Py_SIZE(self)`,
# it was used without a critical section.

d = deque(range(100))

def index():
for _ in range(10000):
try:
d.index(50)
except ValueError:
pass

def mutate():
for _ in range(10000):
d.append(0)
d.clear()
d.extend(range(100))
d.appendleft(-1)

threading_helper.run_concurrently(
[index, *[mutate for _ in range(3)]],
)


if __name__ == "__main__":
unittest.main()
50 changes: 50 additions & 0 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -6615,6 +6615,56 @@ def test_rollover(self):
print(tf.read())
self.assertTrue(found, msg=msg)

@unittest.skipUnless(support.has_st_birthtime,
"st_birthtime not available or supported by Python on this OS")
def test_rollover_based_on_st_birthtime_only(self):
def add_record(message: str) -> None:
fh = logging.handlers.TimedRotatingFileHandler(
self.fn, when='S', interval=4, encoding="utf-8", backupCount=1)
fmt = logging.Formatter('%(asctime)s %(message)s')
fh.setFormatter(fmt)
record = logging.makeLogRecord({'msg': message})
fh.emit(record)
fh.close()

add_record('testing - initial')
self.assertLogFile(self.fn)
# Sleep a little over the half of rollover time - and this value
# must be over 2 seconds, since this is the mtime resolution on
# FAT32 filesystems.
time.sleep(2.1)
add_record('testing - update before rollover to renew the st_mtime')
time.sleep(2.1) # a little over the half of rollover time
add_record('testing - new record supposedly in the new file after rollover')

# At this point, the log file should be rotated if the rotation
# is based on creation time but should be not if it's based on
# creation time.
found = False
now = datetime.datetime.now()
GO_BACK = 5 # seconds
for secs in range(GO_BACK):
prev = now - datetime.timedelta(seconds=secs)
fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
found = os.path.exists(fn)
if found:
self.rmfiles.append(fn)
break
msg = 'No rotated files found, went back %d seconds' % GO_BACK
if not found:
# print additional diagnostics
dn, fn = os.path.split(self.fn)
files = [f for f in os.listdir(dn) if f.startswith(fn)]
print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
print('The only matching files are: %s' % files, file=sys.stderr)
for f in files:
print('Contents of %s:' % f)
path = os.path.join(dn, f)
print(os.stat(path))
with open(path, 'r') as tf:
print(tf.read())
self.assertTrue(found, msg=msg)

def test_rollover_at_midnight(self, weekly=False):
os_helper.unlink(self.fn)
now = datetime.datetime.now()
Expand Down
12 changes: 12 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,18 @@ def test_blob_set_empty_slice(self):
self.blob[0:0] = b""
self.assertEqual(self.blob[:], self.data)

def test_blob_set_empty_slice_wrong_type(self):
with self.assertRaises(TypeError):
self.blob[5:5] = None

def test_blob_set_empty_slice_wrong_size(self):
with self.assertRaisesRegex(IndexError, "wrong size"):
self.blob[5:5] = b"123"

def test_blob_set_empty_slice_correct(self):
self.blob[5:5] = b""
self.assertEqual(self.blob[:], self.data)

def test_blob_set_slice_with_skip(self):
self.blob[0:10:2] = b"12345"
actual = self.cx.execute("select b from test").fetchone()[0]
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,7 @@ Owen Martin
Sidney San Martín
Westley Martínez
Sébastien Martini
Iván Márton
Roger Masse
Nick Mathewson
Simon Mathieu
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
A bug has been fixed that made the ``TimedRotatingFileHandler`` use the
MTIME attribute of the configured log file to to detect whether it has to be
rotated yet or not. In cases when the file was changed within the rotation
period the value of the MTIME was also updated to the current time and as a
result the rotation never happened. The file creation time (CTIME) is used
instead that makes the rotation file modification independent.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a race condition in :meth:`collections.deque.index` with free-threading.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Speed up :func:`logging.getLogger` with a lock-free fast path that returns an
already-registered logger without acquiring the logging lock. Patch by Bernát
Gábor.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix :class:`sqlite3.Blob` slice assignment to raise
:exc:`TypeError` and :exc:`IndexError` for type and size mismatches
respectively, even when the target slice is empty.
15 changes: 8 additions & 7 deletions Modules/_collectionsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ _collections.deque.index as deque_index
deque: dequeobject
value as v: object
start: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='0') = NULL
stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='Py_SIZE(deque)') = NULL
stop: object(converter='_PyEval_SliceIndexNotNone', type='Py_ssize_t', c_default='PY_SSIZE_T_MAX') = NULL
/

Return first index of value.
Expand All @@ -1262,30 +1262,31 @@ Raises ValueError if the value is not present.
static PyObject *
deque_index_impl(dequeobject *deque, PyObject *v, Py_ssize_t start,
Py_ssize_t stop)
/*[clinic end generated code: output=df45132753175ef9 input=90f48833a91e1743]*/
/*[clinic end generated code: output=df45132753175ef9 input=1c3b19632cf3484f]*/
{
Py_ssize_t i, n;
PyObject *item;
block *b = deque->leftblock;
Py_ssize_t index = deque->leftindex;
size_t start_state = deque->state;
int cmp;
Py_ssize_t size = Py_SIZE(deque);

if (start < 0) {
start += Py_SIZE(deque);
start += size;
if (start < 0)
start = 0;
}
if (stop < 0) {
stop += Py_SIZE(deque);
stop += size;
if (stop < 0)
stop = 0;
}
if (stop > Py_SIZE(deque))
stop = Py_SIZE(deque);
if (stop > size)
stop = size;
if (start > stop)
start = stop;
assert(0 <= start && start <= stop && stop <= Py_SIZE(deque));
assert(0 <= start && start <= stop && stop <= size);

for (i=0 ; i < start - BLOCKLEN ; i += BLOCKLEN) {
b = b->rightlink;
Expand Down
16 changes: 10 additions & 6 deletions Modules/_sqlite/blob.c
Original file line number Diff line number Diff line change
Expand Up @@ -531,21 +531,25 @@ ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value)
return -1;
}

if (len == 0) {
return 0;
}

Py_buffer vbuf;
if (PyObject_GetBuffer(value, &vbuf, PyBUF_SIMPLE) < 0) {
return -1;
}

int rc = -1;
if (vbuf.len != len) {
PyErr_SetString(PyExc_IndexError,
"Blob slice assignment is wrong size");
PyBuffer_Release(&vbuf);
return -1;
}
else if (step == 1) {

if (len == 0) {
PyBuffer_Release(&vbuf);
return 0;
}

int rc = -1;
if (step == 1) {
rc = inner_write(self, vbuf.buf, len, start);
}
else {
Expand Down
4 changes: 2 additions & 2 deletions Modules/clinic/_collectionsmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3123,7 +3123,7 @@ class path_t_converter(CConverter):
impl_by_reference = True
parse_by_reference = True
default_type = ()
c_init_default = "<placeholder>" # overridden in pre_render(()
c_init_default = "<placeholder>" # overridden in pre_render()

converter = 'path_converter'

Expand Down Expand Up @@ -3266,7 +3266,7 @@ class confname_converter(CConverter):
""", argname=argname, converter=self.converter, table=self.table)

[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=d58f18bdf3bd3565]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=ddbf3ac90a981122]*/

/*[clinic input]

Expand Down
4 changes: 4 additions & 0 deletions Tools/jit/template.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@

#include "Python.h"

#ifndef NDEBUG
#undef assert
#define assert(TEST) ((TEST) ? 0 : _Py_jit_assertion_failure(__LINE__))
#endif

#include "pycore_backoff.h"
#include "pycore_call.h"
Expand Down Expand Up @@ -40,8 +42,10 @@

#include "jit.h"

#ifndef NDEBUG
#undef assert
#define assert(TEST) ((TEST) ? 0 : _Py_jit_assertion_failure(__LINE__))
#endif

#undef CURRENT_OPERAND0_64
#define CURRENT_OPERAND0_64() (_operand0_64)
Expand Down
Loading