Skip to content

Commit

Permalink
Merge branch 'main' into path-move
Browse files Browse the repository at this point in the history
  • Loading branch information
barneygale authored Aug 24, 2024
2 parents 175d687 + c4ee4e7 commit 8182a88
Show file tree
Hide file tree
Showing 12 changed files with 653 additions and 405 deletions.
4 changes: 2 additions & 2 deletions Doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ dist:
# as otherwise the full latexmk process is run twice.
# ($$ is needed to escape the $; https://www.gnu.org/software/make/manual/make.html#Basics-of-Variable-References)
-sed -i 's/: all-$$(FMT)/:/' build/latex/Makefile
(cd build/latex; $(MAKE) clean && $(MAKE) --jobs=$((`nproc`+1)) --output-sync LATEXMKOPTS='-quiet' all-pdf && $(MAKE) FMT=pdf zip bz2)
(cd build/latex; $(MAKE) clean && $(MAKE) --jobs=$$((`nproc`+1)) --output-sync LATEXMKOPTS='-quiet' all-pdf && $(MAKE) FMT=pdf zip bz2)
cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-a4.zip
cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2
@echo "Build finished and archived!"
Expand All @@ -227,7 +227,7 @@ dist:
rm -rf build/latex
$(MAKE) latex PAPER=letter
-sed -i 's/: all-$$(FMT)/:/' build/latex/Makefile
(cd build/latex; $(MAKE) clean && $(MAKE) --jobs=$((`nproc`+1)) --output-sync LATEXMKOPTS='-quiet' all-pdf && $(MAKE) FMT=pdf zip bz2)
(cd build/latex; $(MAKE) clean && $(MAKE) --jobs=$$((`nproc`+1)) --output-sync LATEXMKOPTS='-quiet' all-pdf && $(MAKE) FMT=pdf zip bz2)
cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-letter.zip
cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-letter.tar.bz2
@echo "Build finished and archived!"
Expand Down
2 changes: 2 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3892,6 +3892,8 @@ features:
.. versionadded:: 3.10


.. _os-timerfd:

Timer File Descriptors
~~~~~~~~~~~~~~~~~~~~~~

Expand Down
833 changes: 478 additions & 355 deletions Doc/whatsnew/3.13.rst

Large diffs are not rendered by default.

21 changes: 9 additions & 12 deletions Include/internal/pycore_stackref.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,15 @@ PyStackRef_FromPyObjectImmortal(PyObject *obj)
} while (0)

#ifdef Py_GIL_DISABLED
static inline void
PyStackRef_CLOSE(_PyStackRef stackref)
{
if (PyStackRef_IsDeferred(stackref)) {
// No assert for being immortal or deferred here.
// The GC unsets deferred objects right before clearing.
return;
}
Py_DECREF(PyStackRef_AsPyObjectBorrow(stackref));
}
# define PyStackRef_CLOSE(REF) \
do { \
_PyStackRef _close_tmp = (REF); \
if (!PyStackRef_IsDeferred(_close_tmp)) { \
Py_DECREF(PyStackRef_AsPyObjectBorrow(_close_tmp)); \
} \
} while (0)
#else
# define PyStackRef_CLOSE(stackref) Py_DECREF(PyStackRef_AsPyObjectBorrow(stackref));
# define PyStackRef_CLOSE(stackref) Py_DECREF(PyStackRef_AsPyObjectBorrow(stackref))
#endif

#define PyStackRef_XCLOSE(stackref) \
Expand All @@ -227,7 +224,7 @@ PyStackRef_DUP(_PyStackRef stackref)
return stackref;
}
#else
# define PyStackRef_DUP(stackref) PyStackRef_FromPyObjectSteal(Py_NewRef(PyStackRef_AsPyObjectBorrow(stackref)));
# define PyStackRef_DUP(stackref) PyStackRef_FromPyObjectSteal(Py_NewRef(PyStackRef_AsPyObjectBorrow(stackref)))
#endif

static inline void
Expand Down
58 changes: 42 additions & 16 deletions Lib/pathlib/_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
_winapi = None


def get_copy_blocksize(infd):
def _get_copy_blocksize(infd):
"""Determine blocksize for fastcopying on Linux.
Hopefully the whole file will be copied in a single call.
The copying itself should be performed in a loop 'till EOF is
Expand All @@ -40,56 +40,64 @@ def get_copy_blocksize(infd):


if fcntl and hasattr(fcntl, 'FICLONE'):
def clonefd(source_fd, target_fd):
def _ficlone(source_fd, target_fd):
"""
Perform a lightweight copy of two files, where the data blocks are
copied only when modified. This is known as Copy on Write (CoW),
instantaneous copy or reflink.
"""
fcntl.ioctl(target_fd, fcntl.FICLONE, source_fd)
else:
clonefd = None
_ficlone = None


if posix and hasattr(posix, '_fcopyfile'):
def copyfd(source_fd, target_fd):
def _fcopyfile(source_fd, target_fd):
"""
Copy a regular file content using high-performance fcopyfile(3)
syscall (macOS).
"""
posix._fcopyfile(source_fd, target_fd, posix._COPYFILE_DATA)
elif hasattr(os, 'copy_file_range'):
def copyfd(source_fd, target_fd):
else:
_fcopyfile = None


if hasattr(os, 'copy_file_range'):
def _copy_file_range(source_fd, target_fd):
"""
Copy data from one regular mmap-like fd to another by using a
high-performance copy_file_range(2) syscall that gives filesystems
an opportunity to implement the use of reflinks or server-side
copy.
This should work on Linux >= 4.5 only.
"""
blocksize = get_copy_blocksize(source_fd)
blocksize = _get_copy_blocksize(source_fd)
offset = 0
while True:
sent = os.copy_file_range(source_fd, target_fd, blocksize,
offset_dst=offset)
if sent == 0:
break # EOF
offset += sent
elif hasattr(os, 'sendfile'):
def copyfd(source_fd, target_fd):
else:
_copy_file_range = None


if hasattr(os, 'sendfile'):
def _sendfile(source_fd, target_fd):
"""Copy data from one regular mmap-like fd to another by using
high-performance sendfile(2) syscall.
This should work on Linux >= 2.6.33 only.
"""
blocksize = get_copy_blocksize(source_fd)
blocksize = _get_copy_blocksize(source_fd)
offset = 0
while True:
sent = os.sendfile(target_fd, source_fd, offset, blocksize)
if sent == 0:
break # EOF
offset += sent
else:
copyfd = None
_sendfile = None


if _winapi and hasattr(_winapi, 'CopyFile2'):
Expand All @@ -114,18 +122,36 @@ def copyfileobj(source_f, target_f):
else:
try:
# Use OS copy-on-write where available.
if clonefd:
if _ficlone:
try:
clonefd(source_fd, target_fd)
_ficlone(source_fd, target_fd)
return
except OSError as err:
if err.errno not in (EBADF, EOPNOTSUPP, ETXTBSY, EXDEV):
raise err

# Use OS copy where available.
if copyfd:
copyfd(source_fd, target_fd)
return
if _fcopyfile:
try:
_fcopyfile(source_fd, target_fd)
return
except OSError as err:
if err.errno not in (EINVAL, ENOTSUP):
raise err
if _copy_file_range:
try:
_copy_file_range(source_fd, target_fd)
return
except OSError as err:
if err.errno not in (ETXTBSY, EXDEV):
raise err
if _sendfile:
try:
_sendfile(source_fd, target_fd)
return
except OSError as err:
if err.errno != ENOTSOCK:
raise err
except OSError as err:
# Produce more useful error messages.
err.filename = source_f.name
Expand Down
48 changes: 48 additions & 0 deletions Lib/test/test_pathlib/test_pathlib.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import io
import os
import sys
Expand All @@ -22,10 +23,18 @@
from test.test_pathlib import test_pathlib_abc
from test.test_pathlib.test_pathlib_abc import needs_posix, needs_windows, needs_symlinks

try:
import fcntl
except ImportError:
fcntl = None
try:
import grp, pwd
except ImportError:
grp = pwd = None
try:
import posix
except ImportError:
posix = None


root_in_posix = False
Expand Down Expand Up @@ -720,6 +729,45 @@ def test_copy_link_preserve_metadata(self):
if hasattr(source_st, 'st_flags'):
self.assertEqual(source_st.st_flags, target_st.st_flags)

def test_copy_error_handling(self):
def make_raiser(err):
def raiser(*args, **kwargs):
raise OSError(err, os.strerror(err))
return raiser

base = self.cls(self.base)
source = base / 'fileA'
target = base / 'copyA'

# Raise non-fatal OSError from all available fast copy functions.
with contextlib.ExitStack() as ctx:
if fcntl and hasattr(fcntl, 'FICLONE'):
ctx.enter_context(mock.patch('fcntl.ioctl', make_raiser(errno.EXDEV)))
if posix and hasattr(posix, '_fcopyfile'):
ctx.enter_context(mock.patch('posix._fcopyfile', make_raiser(errno.ENOTSUP)))
if hasattr(os, 'copy_file_range'):
ctx.enter_context(mock.patch('os.copy_file_range', make_raiser(errno.EXDEV)))
if hasattr(os, 'sendfile'):
ctx.enter_context(mock.patch('os.sendfile', make_raiser(errno.ENOTSOCK)))

source.copy(target)
self.assertTrue(target.exists())
self.assertEqual(source.read_text(), target.read_text())

# Raise fatal OSError from first available fast copy function.
if fcntl and hasattr(fcntl, 'FICLONE'):
patchpoint = 'fcntl.ioctl'
elif posix and hasattr(posix, '_fcopyfile'):
patchpoint = 'posix._fcopyfile'
elif hasattr(os, 'copy_file_range'):
patchpoint = 'os.copy_file_range'
elif hasattr(os, 'sendfile'):
patchpoint = 'os.sendfile'
else:
return
with mock.patch(patchpoint, make_raiser(errno.ENOENT)):
self.assertRaises(FileNotFoundError, source.copy, target)

@unittest.skipIf(sys.platform == "win32" or sys.platform == "wasi", "directories are always readable on Windows and WASI")
@unittest.skipIf(root_in_posix, "test fails with root privilege")
def test_copy_dir_no_read_permission(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix memory leak in :mod:`!_decimal`.
26 changes: 24 additions & 2 deletions Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,10 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
CtxCaps(self) = 1;
self->tstate = NULL;

if (type == state->PyDecContext_Type) {
PyObject_GC_Track(self);
}
assert(PyObject_GC_IsTracked((PyObject *)self));
return (PyObject *)self;
}

Expand Down Expand Up @@ -2038,6 +2042,10 @@ PyDecType_New(PyTypeObject *type)
MPD(dec)->alloc = _Py_DEC_MINALLOC;
MPD(dec)->data = dec->data;

if (type == state->PyDec_Type) {
PyObject_GC_Track(dec);
}
assert(PyObject_GC_IsTracked((PyObject *)dec));
return (PyObject *)dec;
}
#define dec_alloc(st) PyDecType_New((st)->PyDec_Type)
Expand Down Expand Up @@ -6143,8 +6151,22 @@ decimal_clear(PyObject *module)
Py_CLEAR(state->SignalTuple);
Py_CLEAR(state->PyDecimal);

PyMem_Free(state->signal_map);
PyMem_Free(state->cond_map);
if (state->signal_map != NULL) {
for (DecCondMap *cm = state->signal_map; cm->name != NULL; cm++) {
Py_DECREF(cm->ex);
}
PyMem_Free(state->signal_map);
state->signal_map = NULL;
}

if (state->cond_map != NULL) {
// cond_map[0].ex has borrowed a reference from signal_map[0].ex
for (DecCondMap *cm = state->cond_map + 1; cm->name != NULL; cm++) {
Py_DECREF(cm->ex);
}
PyMem_Free(state->cond_map);
state->cond_map = NULL;
}
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2215,7 +2215,7 @@ dummy_func(
*value_ptr = PyStackRef_AsPyObjectSteal(value);
if (old_value == NULL) {
PyDictValues *values = _PyObject_InlineValues(owner_o);
int index = value_ptr - values->values;
Py_ssize_t index = value_ptr - values->values;
_PyDictValues_AddToInsertionOrder(values, index);
}
else {
Expand Down
Loading

0 comments on commit 8182a88

Please sign in to comment.