Skip to content

Commit 82db572

Browse files
authored
pythongh-116622: Fix testPyObjectPrintOSError on Android (python#122487)
Adds extra handling for way BSD/Android return errors from calls to fwrite.
1 parent 29c04df commit 82db572

File tree

4 files changed

+39
-9
lines changed

4 files changed

+39
-9
lines changed

Android/android.py

+19-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22

33
import argparse
4+
from glob import glob
45
import os
56
import re
67
import shutil
@@ -16,16 +17,21 @@
1617
CROSS_BUILD_DIR = CHECKOUT / "cross-build"
1718

1819

19-
def delete_if_exists(path):
20-
if path.exists():
20+
def delete_glob(pattern):
21+
# Path.glob doesn't accept non-relative patterns.
22+
for path in glob(str(pattern)):
23+
path = Path(path)
2124
print(f"Deleting {path} ...")
22-
shutil.rmtree(path)
25+
if path.is_dir() and not path.is_symlink():
26+
shutil.rmtree(path)
27+
else:
28+
path.unlink()
2329

2430

2531
def subdir(name, *, clean=None):
2632
path = CROSS_BUILD_DIR / name
2733
if clean:
28-
delete_if_exists(path)
34+
delete_glob(path)
2935
if not path.exists():
3036
if clean is None:
3137
sys.exit(
@@ -150,10 +156,17 @@ def configure_host_python(context):
150156

151157

152158
def make_host_python(context):
159+
# The CFLAGS and LDFLAGS set in android-env include the prefix dir, so
160+
# delete any previously-installed Python libs and include files to prevent
161+
# them being used during the build.
153162
host_dir = subdir(context.host)
163+
prefix_dir = host_dir / "prefix"
164+
delete_glob(f"{prefix_dir}/include/python*")
165+
delete_glob(f"{prefix_dir}/lib/libpython*")
166+
154167
os.chdir(host_dir / "build")
155168
run(["make", "-j", str(os.cpu_count())], host=context.host)
156-
run(["make", "install", f"prefix={host_dir}/prefix"], host=context.host)
169+
run(["make", "install", f"prefix={prefix_dir}"], host=context.host)
157170

158171

159172
def build_all(context):
@@ -164,7 +177,7 @@ def build_all(context):
164177

165178

166179
def clean_all(context):
167-
delete_if_exists(CROSS_BUILD_DIR)
180+
delete_glob(CROSS_BUILD_DIR)
168181

169182

170183
# To avoid distributing compiled artifacts without corresponding source code,

Android/testbed/app/build.gradle.kts

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@ plugins {
77

88
val PYTHON_DIR = File(projectDir, "../../..").canonicalPath
99
val PYTHON_CROSS_DIR = "$PYTHON_DIR/cross-build"
10+
1011
val ABIS = mapOf(
1112
"arm64-v8a" to "aarch64-linux-android",
1213
"x86_64" to "x86_64-linux-android",
13-
)
14+
).filter { File("$PYTHON_CROSS_DIR/${it.value}").exists() }
15+
if (ABIS.isEmpty()) {
16+
throw GradleException(
17+
"No Android ABIs found in $PYTHON_CROSS_DIR: see Android/README.md " +
18+
"for building instructions."
19+
)
20+
}
1421

1522
val PYTHON_VERSION = File("$PYTHON_DIR/Include/patchlevel.h").useLines {
1623
for (line in it) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Make :any:`PyObject_Print` work around a bug in Android and OpenBSD which
2+
prevented it from throwing an exception when trying to write to a read-only
3+
stream.

Objects/object.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ int
536536
PyObject_Print(PyObject *op, FILE *fp, int flags)
537537
{
538538
int ret = 0;
539+
int write_error = 0;
539540
if (PyErr_CheckSignals())
540541
return -1;
541542
#ifdef USE_STACKCHECK
@@ -574,14 +575,20 @@ PyObject_Print(PyObject *op, FILE *fp, int flags)
574575
ret = -1;
575576
}
576577
else {
577-
fwrite(t, 1, len, fp);
578+
/* Versions of Android and OpenBSD from before 2023 fail to
579+
set the `ferror` indicator when writing to a read-only
580+
stream, so we need to check the return value.
581+
(https://github.com/openbsd/src/commit/fc99cf9338942ecd9adc94ea08bf6188f0428c15) */
582+
if (fwrite(t, 1, len, fp) != (size_t)len) {
583+
write_error = 1;
584+
}
578585
}
579586
Py_DECREF(s);
580587
}
581588
}
582589
}
583590
if (ret == 0) {
584-
if (ferror(fp)) {
591+
if (write_error || ferror(fp)) {
585592
PyErr_SetFromErrno(PyExc_OSError);
586593
clearerr(fp);
587594
ret = -1;

0 commit comments

Comments
 (0)