diff --git a/demo/app/build.gradle.kts b/demo/app/build.gradle.kts index 4312006aa0..c812a02dc0 100644 --- a/demo/app/build.gradle.kts +++ b/demo/app/build.gradle.kts @@ -149,6 +149,9 @@ for (path in listOf( } dependencies { + // See TestAndroidReflect. + compileOnly(project(":compileOnly")) + // Keep these versions the same as the pkgtest app. implementation("androidx.appcompat:appcompat:1.7.1") implementation("androidx.constraintlayout:constraintlayout:1.1.3") diff --git a/demo/app/src/main/java/com/chaquo/python/demo/TestAndroidReflect.java b/demo/app/src/main/java/com/chaquo/python/demo/TestAndroidReflect.java index c5f4845210..11fd46d066 100644 --- a/demo/app/src/main/java/com/chaquo/python/demo/TestAndroidReflect.java +++ b/demo/app/src/main/java/com/chaquo/python/demo/TestAndroidReflect.java @@ -1,14 +1,16 @@ package com.chaquo.python.demo; -import android.view.textclassifier.*; +// Some of the members of this class refer to classes which don't exist at runtime +// (see test_reflect.py). This is done by putting them in a compileOnly dependency in +// build.gradle.kts. @SuppressWarnings("unused") public class TestAndroidReflect { - public TextClassifier tcFieldPublic; - protected TextClassifier tcFieldProtected; + public CompileOnly coFieldPublic; + protected CompileOnly coFieldProtected; - public TextClassifier tcMethodPublic() { return null; } - protected TextClassifier tcMethodProtected() { return null; } + public CompileOnly coMethodPublic() { return null; } + protected CompileOnly coMethodProtected() { return null; } public int iFieldPublic; protected int iFieldProtected; diff --git a/demo/compileOnly/.gitignore b/demo/compileOnly/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/demo/compileOnly/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/demo/compileOnly/build.gradle.kts b/demo/compileOnly/build.gradle.kts new file mode 100644 index 0000000000..8c97e420a9 --- /dev/null +++ b/demo/compileOnly/build.gradle.kts @@ -0,0 +1,9 @@ +// See TestAndroidReflect. + +plugins { + id("java-library") +} +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} diff --git a/demo/compileOnly/src/main/java/com/chaquo/python/demo/CompileOnly.java b/demo/compileOnly/src/main/java/com/chaquo/python/demo/CompileOnly.java new file mode 100644 index 0000000000..768ea48cf7 --- /dev/null +++ b/demo/compileOnly/src/main/java/com/chaquo/python/demo/CompileOnly.java @@ -0,0 +1,5 @@ +package com.chaquo.python.demo; + +// See TestAndroidReflect. +public class CompileOnly { +} \ No newline at end of file diff --git a/demo/settings.gradle.kts b/demo/settings.gradle.kts index f6488b77bc..f3f3fed7c5 100644 --- a/demo/settings.gradle.kts +++ b/demo/settings.gradle.kts @@ -24,3 +24,4 @@ dependencyResolutionManagement { } } include(":app") +include(":compileOnly") diff --git a/product/runtime/src/main/python/java/android/__init__.py b/product/runtime/src/main/python/java/android/__init__.py index 8aba9d42db..55c8d7dab2 100644 --- a/product/runtime/src/main/python/java/android/__init__.py +++ b/product/runtime/src/main/python/java/android/__init__.py @@ -85,12 +85,11 @@ def get_exec_path_override(*args, **kwargs): # these result in an OSError, so the calling code will still work, but it generates # a log message like `avc: denied { ioctl } for path="pipe:[10138300]"`, which can # be a problem if the app is doing it repeatedly. - # - # TODO: remove once fixed upstream (https://github.com/python/cpython/pull/154885). - def get_terminal_size_override(*args, **kwargs): - error = errno.ENOTTY - raise OSError(error, os.strerror(error)) - os.get_terminal_size = get_terminal_size_override + if sys.version_info < (3, 15): # https://github.com/python/cpython/pull/154885 + def get_terminal_size_override(*args, **kwargs): + error = errno.ENOTTY + raise OSError(error, os.strerror(error)) + os.get_terminal_size = get_terminal_size_override def initialize_tempfile(): diff --git a/product/runtime/src/test/python/chaquopy/test/android/test_stream.py b/product/runtime/src/test/python/chaquopy/test/android/test_stream.py index 4c34941f08..e2dc2167e6 100644 --- a/product/runtime/src/test/python/chaquopy/test/android/test_stream.py +++ b/product/runtime/src/test/python/chaquopy/test/android/test_stream.py @@ -18,7 +18,7 @@ "java.android.stream" if sys.version_info < (3, 13) else "_android_support") TextLogStream = import_module(stream_mod_name).TextLogStream -from ..test_utils import FilterWarningsCase +from ..test_utils import API_LEVEL as api_level, FilterWarningsCase # (name, level, fileno) @@ -30,6 +30,10 @@ redirected_native = False +# Some API levels filter out consecutive identical lines and replace them with a +# "chatty" marker. +chatty_issue = (26 <= api_level <= 30) + class TestAndroidOutput(FilterWarningsCase): maxDiff = None @@ -172,15 +176,13 @@ def write(s, lines=None, *, write_len=None): write("\u0000b", [r"\xc0\x80b"]) write("a\u0000b", [r"a\xc0\x80b"]) - # Multi-line messages. Avoid identical consecutive lines, as - # they may activate "chatty" filtering and break the tests. - # - # Additional spaces will appear in the output where necessary to - # protect leading newlines. + # Multi-line messages. Additional spaces will appear in the output where + # necessary to protect leading newlines. write("\nx", [" "]) write("\na\n", ["x", "a"]) write("\n", [" "]) - write("\n\n", [" ", " "]) + if not chatty_issue: + write("\n\n", [" ", " "]) write("b\n", ["b"]) write("c\n\n", ["c", " "]) write("d\ne", ["d"]) @@ -199,7 +201,8 @@ def write(s, lines=None, *, write_len=None): write("\nx", [" ", "x"]) write("\na\n", [" ", "a"]) write("\n", [" "]) - write("\n\n", [" ", " "]) + if not chatty_issue: + write("\n\n", [" ", " "]) write("b\n", ["b"]) write("c\n\n", ["c", " "]) write("d\ne", ["d", "e"]) diff --git a/product/runtime/src/test/python/chaquopy/test/test_reflect.py b/product/runtime/src/test/python/chaquopy/test/test_reflect.py index 761762faae..e59bfd3313 100644 --- a/product/runtime/src/test/python/chaquopy/test/test_reflect.py +++ b/product/runtime/src/test/python/chaquopy/test/test_reflect.py @@ -561,20 +561,16 @@ def multi_fi(self, *interfaces): @skipIf(not API_LEVEL, "Android only") class TestAndroidReflect(FilterWarningsCase): - MEMBERS = ["tcFieldPublic", "tcFieldProtected", "tcMethodPublic", "tcMethodProtected", + MEMBERS = ["coFieldPublic", "coFieldProtected", "coMethodPublic", "coMethodProtected", "iFieldPublic", "iFieldProtected", "iMethodPublic", "iMethodProtected", "finalize"] def test_android_reflect(self): - from com.chaquo.python.demo import TestAndroidReflect as TAR + from com.chaquo.python.demo import TestAndroidReflect - if API_LEVEL >= 26: - # TextClassifier is in the platform, so all members should be visible. - self.assertMembers(TAR, self.MEMBERS) - else: - # Overridden methods should be visible, plus public methods that don't involve - # TextClassifier. - self.assertMembers(TAR, ["iMethodPublic", "finalize"]) + # Overridden methods should be visible, plus public methods that don't involve + # TextClassifier. + self.assertMembers(TestAndroidReflect, ["iMethodPublic", "finalize"]) def assertMembers(self, cls, names): for name in names: @@ -583,11 +579,6 @@ def assertMembers(self, cls, names): for name in self.MEMBERS: if name not in names: - # With AGP 9.0, ProGuard causes this method to become visible for some - # reason. See also TestReflect.test_access. - if name == "iMethodProtected": - continue - with self.subTest(name=name): self.assertFalse(self.declares_member(cls, name))