Skip to content

Commit 720d6e5

Browse files
committed
Merge branch 'main' into gh-129605
2 parents 418b1b9 + 808071b commit 720d6e5

25 files changed

+1566
-748
lines changed

Doc/c-api/init.rst

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,8 @@ Process-wide parameters
622622
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
623623
624624
.. deprecated-removed:: 3.13 3.15
625-
Get :data:`sys.executable` instead.
625+
Use :c:func:`PyConfig_Get("executable") <PyConfig_Get>`
626+
(:data:`sys.executable`) instead.
626627
627628
628629
.. c:function:: wchar_t* Py_GetPrefix()
@@ -644,8 +645,10 @@ Process-wide parameters
644645
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
645646
646647
.. deprecated-removed:: 3.13 3.15
647-
Get :data:`sys.base_prefix` instead, or :data:`sys.prefix` if
648-
:ref:`virtual environments <venv-def>` need to be handled.
648+
Use :c:func:`PyConfig_Get("base_prefix") <PyConfig_Get>`
649+
(:data:`sys.base_prefix`) instead. Use :c:func:`PyConfig_Get("prefix")
650+
<PyConfig_Get>` (:data:`sys.prefix`) if :ref:`virtual environments
651+
<venv-def>` need to be handled.
649652
650653
651654
.. c:function:: wchar_t* Py_GetExecPrefix()
@@ -690,9 +693,11 @@ Process-wide parameters
690693
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
691694

692695
.. deprecated-removed:: 3.13 3.15
693-
Get :data:`sys.base_exec_prefix` instead, or :data:`sys.exec_prefix` if
694-
:ref:`virtual environments <venv-def>` need to be handled.
695-
696+
Use :c:func:`PyConfig_Get("base_exec_prefix") <PyConfig_Get>`
697+
(:data:`sys.base_exec_prefix`) instead. Use
698+
:c:func:`PyConfig_Get("exec_prefix") <PyConfig_Get>`
699+
(:data:`sys.exec_prefix`) if :ref:`virtual environments <venv-def>` need
700+
to be handled.
696701

697702
.. c:function:: wchar_t* Py_GetProgramFullPath()
698703
@@ -712,7 +717,8 @@ Process-wide parameters
712717
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
713718

714719
.. deprecated-removed:: 3.13 3.15
715-
Get :data:`sys.executable` instead.
720+
Use :c:func:`PyConfig_Get("executable") <PyConfig_Get>`
721+
(:data:`sys.executable`) instead.
716722

717723

718724
.. c:function:: wchar_t* Py_GetPath()
@@ -740,8 +746,8 @@ Process-wide parameters
740746
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
741747

742748
.. deprecated-removed:: 3.13 3.15
743-
Get :data:`sys.path` instead.
744-
749+
Use :c:func:`PyConfig_Get("module_search_paths") <PyConfig_Get>`
750+
(:data:`sys.path`) instead.
745751

746752
.. c:function:: const char* Py_GetVersion()
747753
@@ -926,8 +932,8 @@ Process-wide parameters
926932
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
927933
928934
.. deprecated-removed:: 3.13 3.15
929-
Get :c:member:`PyConfig.home` or :envvar:`PYTHONHOME` environment
930-
variable instead.
935+
Use :c:func:`PyConfig_Get("home") <PyConfig_Get>` or the
936+
:envvar:`PYTHONHOME` environment variable instead.
931937
932938
933939
.. _threads:

Doc/c-api/module.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -523,9 +523,6 @@ state:
523523
524524
On success, return ``0``. On error, raise an exception and return ``-1``.
525525
526-
Return ``-1`` if *value* is ``NULL``. It must be called with an exception
527-
raised in this case.
528-
529526
Example usage::
530527
531528
static int
@@ -540,6 +537,10 @@ state:
540537
return res;
541538
}
542539
540+
To be convenient, the function accepts ``NULL`` *value* with an exception
541+
set. In this case, return ``-1`` and just leave the raised exception
542+
unchanged.
543+
543544
The example can also be written without checking explicitly if *obj* is
544545
``NULL``::
545546

Doc/deprecations/c-api-pending-removal-in-3.15.rst

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,35 @@ Pending removal in Python 3.15
1010
:c:func:`PyWeakref_GetRef` on Python 3.12 and older.
1111
* :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro:
1212
Use :c:type:`wchar_t` instead.
13-
* Python initialization functions:
13+
* Python initialization functions, deprecated in Python 3.13:
1414

15-
* :c:func:`PySys_ResetWarnOptions`:
16-
Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead.
17-
* :c:func:`Py_GetExecPrefix`:
18-
Get :data:`sys.base_exec_prefix` and :data:`sys.exec_prefix` instead.
1915
* :c:func:`Py_GetPath`:
20-
Get :data:`sys.path` instead.
16+
Use :c:func:`PyConfig_Get("module_search_paths") <PyConfig_Get>`
17+
(:data:`sys.path`) instead.
2118
* :c:func:`Py_GetPrefix`:
22-
Get :data:`sys.base_prefix` and :data:`sys.prefix` instead.
19+
Use :c:func:`PyConfig_Get("base_prefix") <PyConfig_Get>`
20+
(:data:`sys.base_prefix`) instead. Use :c:func:`PyConfig_Get("prefix")
21+
<PyConfig_Get>` (:data:`sys.prefix`) if :ref:`virtual environments
22+
<venv-def>` need to be handled.
23+
* :c:func:`Py_GetExecPrefix`:
24+
Use :c:func:`PyConfig_Get("base_exec_prefix") <PyConfig_Get>`
25+
(:data:`sys.base_exec_prefix`) instead. Use
26+
:c:func:`PyConfig_Get("exec_prefix") <PyConfig_Get>`
27+
(:data:`sys.exec_prefix`) if :ref:`virtual environments <venv-def>` need to
28+
be handled.
2329
* :c:func:`Py_GetProgramFullPath`:
24-
Get :data:`sys.executable` instead.
30+
Use :c:func:`PyConfig_Get("executable") <PyConfig_Get>`
31+
(:data:`sys.executable`) instead.
2532
* :c:func:`Py_GetProgramName`:
26-
Get :data:`sys.executable` instead.
33+
Use :c:func:`PyConfig_Get("executable") <PyConfig_Get>`
34+
(:data:`sys.executable`) instead.
2735
* :c:func:`Py_GetPythonHome`:
28-
Get :c:func:`PyConfig_Get("home") <PyConfig_Get>`
29-
or the :envvar:`PYTHONHOME` environment variable instead.
36+
Use :c:func:`PyConfig_Get("home") <PyConfig_Get>` or the
37+
:envvar:`PYTHONHOME` environment variable instead.
3038

31-
See also the :c:func:`PyConfig_Get` function.
39+
The `pythoncapi-compat project
40+
<https://github.com/python/pythoncapi-compat/>`__ can be used to get
41+
:c:func:`PyConfig_Get` on Python 3.13 and older.
3242

3343
* Functions to configure Python's initialization, deprecated in Python 3.11:
3444

@@ -40,6 +50,8 @@ Pending removal in Python 3.15
4050
Set :c:member:`PyConfig.program_name` instead.
4151
* :c:func:`!Py_SetPythonHome()`:
4252
Set :c:member:`PyConfig.home` instead.
53+
* :c:func:`PySys_ResetWarnOptions`:
54+
Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead.
4355

4456
The :c:func:`Py_InitializeFromConfig` API should be used with
4557
:c:type:`PyConfig` instead.

Doc/library/exceptions.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,13 @@ The following exceptions are the exceptions that are usually raised.
562562

563563
Raised when the interpreter finds an internal error, but the situation does not
564564
look so serious to cause it to abandon all hope. The associated value is a
565-
string indicating what went wrong (in low-level terms).
565+
string indicating what went wrong (in low-level terms). In :term:`CPython`,
566+
this could be raised by incorrectly using Python's C API, such as returning
567+
a ``NULL`` value without an exception set.
566568

567-
You should report this to the author or maintainer of your Python interpreter.
569+
If you're confident that this exception wasn't your fault, or the fault of
570+
a package you're using, you should report this to the author or maintainer
571+
of your Python interpreter.
568572
Be sure to report the version of the Python interpreter (``sys.version``; it is
569573
also printed at the start of an interactive Python session), the exact error
570574
message (the exception's associated value) and if possible the source of the

Doc/library/shutil.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,9 @@ the use of userspace buffers in Python as in "``outfd.write(infd.read())``".
512512

513513
On macOS `fcopyfile`_ is used to copy the file content (not metadata).
514514

515-
On Linux and Solaris :func:`os.sendfile` is used.
515+
On Linux :func:`os.copy_file_range` or :func:`os.sendfile` is used.
516+
517+
On Solaris :func:`os.sendfile` is used.
516518

517519
On Windows :func:`shutil.copyfile` uses a bigger default buffer size (1 MiB
518520
instead of 64 KiB) and a :func:`memoryview`-based variant of
@@ -527,6 +529,10 @@ file then shutil will silently fallback on using less efficient
527529
.. versionchanged:: 3.14
528530
Solaris now uses :func:`os.sendfile`.
529531

532+
.. versionchanged:: next
533+
Copy-on-write or server-side copy may be used internally via
534+
:func:`os.copy_file_range` on supported Linux filesystems.
535+
530536
.. _shutil-copytree-example:
531537

532538
copytree example

Include/internal/pycore_opcode_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

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

Lib/shutil.py

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
# https://bugs.python.org/issue43743#msg393429
5050
_USE_CP_SENDFILE = (hasattr(os, "sendfile")
5151
and sys.platform.startswith(("linux", "android", "sunos")))
52+
_USE_CP_COPY_FILE_RANGE = hasattr(os, "copy_file_range")
5253
_HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS
5354

5455
# CMD defaults in Windows 10
@@ -107,6 +108,66 @@ def _fastcopy_fcopyfile(fsrc, fdst, flags):
107108
else:
108109
raise err from None
109110

111+
def _determine_linux_fastcopy_blocksize(infd):
112+
"""Determine blocksize for fastcopying on Linux.
113+
114+
Hopefully the whole file will be copied in a single call.
115+
The copying itself should be performed in a loop 'till EOF is
116+
reached (0 return) so a blocksize smaller or bigger than the actual
117+
file size should not make any difference, also in case the file
118+
content changes while being copied.
119+
"""
120+
try:
121+
blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8 MiB
122+
except OSError:
123+
blocksize = 2 ** 27 # 128 MiB
124+
# On 32-bit architectures truncate to 1 GiB to avoid OverflowError,
125+
# see gh-82500.
126+
if sys.maxsize < 2 ** 32:
127+
blocksize = min(blocksize, 2 ** 30)
128+
return blocksize
129+
130+
def _fastcopy_copy_file_range(fsrc, fdst):
131+
"""Copy data from one regular mmap-like fd to another by using
132+
a high-performance copy_file_range(2) syscall that gives filesystems
133+
an opportunity to implement the use of reflinks or server-side copy.
134+
135+
This should work on Linux >= 4.5 only.
136+
"""
137+
try:
138+
infd = fsrc.fileno()
139+
outfd = fdst.fileno()
140+
except Exception as err:
141+
raise _GiveupOnFastCopy(err) # not a regular file
142+
143+
blocksize = _determine_linux_fastcopy_blocksize(infd)
144+
offset = 0
145+
while True:
146+
try:
147+
n_copied = os.copy_file_range(infd, outfd, blocksize, offset_dst=offset)
148+
except OSError as err:
149+
# ...in oder to have a more informative exception.
150+
err.filename = fsrc.name
151+
err.filename2 = fdst.name
152+
153+
if err.errno == errno.ENOSPC: # filesystem is full
154+
raise err from None
155+
156+
# Give up on first call and if no data was copied.
157+
if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0:
158+
raise _GiveupOnFastCopy(err)
159+
160+
raise err
161+
else:
162+
if n_copied == 0:
163+
# If no bytes have been copied yet, copy_file_range
164+
# might silently fail.
165+
# https://lore.kernel.org/linux-fsdevel/[email protected]/T/#m05753578c7f7882f6e9ffe01f981bc223edef2b0
166+
if offset == 0:
167+
raise _GiveupOnFastCopy()
168+
break
169+
offset += n_copied
170+
110171
def _fastcopy_sendfile(fsrc, fdst):
111172
"""Copy data from one regular mmap-like fd to another by using
112173
high-performance sendfile(2) syscall.
@@ -128,20 +189,7 @@ def _fastcopy_sendfile(fsrc, fdst):
128189
except Exception as err:
129190
raise _GiveupOnFastCopy(err) # not a regular file
130191

131-
# Hopefully the whole file will be copied in a single call.
132-
# sendfile() is called in a loop 'till EOF is reached (0 return)
133-
# so a bufsize smaller or bigger than the actual file size
134-
# should not make any difference, also in case the file content
135-
# changes while being copied.
136-
try:
137-
blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8MiB
138-
except OSError:
139-
blocksize = 2 ** 27 # 128MiB
140-
# On 32-bit architectures truncate to 1GiB to avoid OverflowError,
141-
# see bpo-38319.
142-
if sys.maxsize < 2 ** 32:
143-
blocksize = min(blocksize, 2 ** 30)
144-
192+
blocksize = _determine_linux_fastcopy_blocksize(infd)
145193
offset = 0
146194
while True:
147195
try:
@@ -266,12 +314,20 @@ def copyfile(src, dst, *, follow_symlinks=True):
266314
except _GiveupOnFastCopy:
267315
pass
268316
# Linux / Android / Solaris
269-
elif _USE_CP_SENDFILE:
270-
try:
271-
_fastcopy_sendfile(fsrc, fdst)
272-
return dst
273-
except _GiveupOnFastCopy:
274-
pass
317+
elif _USE_CP_SENDFILE or _USE_CP_COPY_FILE_RANGE:
318+
# reflink may be implicit in copy_file_range.
319+
if _USE_CP_COPY_FILE_RANGE:
320+
try:
321+
_fastcopy_copy_file_range(fsrc, fdst)
322+
return dst
323+
except _GiveupOnFastCopy:
324+
pass
325+
if _USE_CP_SENDFILE:
326+
try:
327+
_fastcopy_sendfile(fsrc, fdst)
328+
return dst
329+
except _GiveupOnFastCopy:
330+
pass
275331
# Windows, see:
276332
# https://github.com/python/cpython/pull/7160#discussion_r195405230
277333
elif _WINDOWS and file_size > 0:

0 commit comments

Comments
 (0)