diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml index dd5094e9d3..1a761225b4 100644 --- a/.github/actions/setup-python/action.yml +++ b/.github/actions/setup-python/action.yml @@ -15,8 +15,10 @@ runs: # `requires_python` and `marker_python_version`. For all other versions, we let # the GitHub runner pick a micro version, which will be faster because it's # locally cached. + # + # When updating this list, see the comment in ci.yml about integration test + # runner versions. python-version: | - 3.6 3.7 3.8.10 3.9 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0daaec2b33..51442e18e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ defaults: jobs: product: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: smorimoto/tune-github-hosted-runner-network@v1.0.0 - uses: actions/checkout@v4.1.6 @@ -72,7 +72,7 @@ jobs: docs: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: smorimoto/tune-github-hosted-runner-network@v1.0.0 - uses: actions/checkout@v4.1.6 @@ -103,7 +103,7 @@ jobs: gradlePython: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: smorimoto/tune-github-hosted-runner-network@v1.0.0 - uses: actions/checkout@v4.1.6 @@ -120,7 +120,7 @@ jobs: demo: needs: [product] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: smorimoto/tune-github-hosted-runner-network@v1.0.0 - uses: actions/checkout@v4.1.6 @@ -160,12 +160,12 @@ jobs: agp-version: ${{ fromJSON(needs.product.outputs.agp-versions) }} os: [linux, macos, windows] include: - # To avoid clutter in the GitHub UI, OS versions are not added to the `os` - # dimension directly. + # Runners must support all the Python versions in setup-python/action.yml: see + # https://github.com/actions/python-versions/blob/main/versions-manifest.json. - os: linux - runs-on: ubuntu-20.04 # 22.04 has a minimum Python version of 3.7 (see setup-python) + runs-on: ubuntu-22.04 - os: macos - runs-on: macos-12 # 14 has a minimum Python version of 3.8 (see setup-python) + runs-on: macos-12 - os: windows runs-on: windows-2022 diff --git a/product/gradle-plugin/README.md b/product/gradle-plugin/README.md index 3b57144637..48750f44a1 100644 --- a/product/gradle-plugin/README.md +++ b/product/gradle-plugin/README.md @@ -105,10 +105,12 @@ After stable release: * Increment Chaquopy major version if not already done. * Update gradle-plugin/src/main/python/chaquopy/util.py. -* Update `testPython` in gradle-plugin/build.gradle, and run the tests. * In test_gradle_plugin, update `OLD_BUILD_PYTHON_VERSION` and `MIN_BUILD_PYTHON_VERSION`, and run the tests which use them. +* Run `gradle:testPython`. * Update the list of Python versions in .github/actions/setup-python/action.yml. +* See the comment in ci.yml about integration test runner versions, and consider + updating them. * Update android.rst. diff --git a/product/gradle-plugin/build.gradle.kts b/product/gradle-plugin/build.gradle.kts index 3a2cb742c0..b0bffc00b3 100644 --- a/product/gradle-plugin/build.gradle.kts +++ b/product/gradle-plugin/build.gradle.kts @@ -133,17 +133,41 @@ abstract class TestPythonTask : DefaultTask() { } } -tasks.register("testPython") { - pythonVersion = "3.7" // Minimum supported buildPython version - workingDir = "$projectDir/src/test/python" -} -tasks.register("testIntegration") { +tasks.register("testPython") { group = "verification" +}.let { + tasks.named("check") { dependsOn(it) } +} + +var minPythonMinor: Int? = null +var maxPythonMinor: Int? = null +file("src/test/integration/test_gradle_plugin.py").useLines { + for (line in it) { + """MIN_BUILD_PYTHON_VERSION = "3.(\d+)""".toRegex().find(line)?.let { + minPythonMinor = it.groupValues[1].toInt() + } + """MAX_BUILD_PYTHON_VERSION = "3.(\d+)""".toRegex().find(line)?.let { + maxPythonMinor = it.groupValues[1].toInt() + } + } } -tasks.check { - dependsOn("testPython", "testIntegration") +for (pythonMinor in minPythonMinor!! .. maxPythonMinor!!) { + val pythonVer = "3.$pythonMinor" + tasks.register("testPython-$pythonVer") { + pythonVersion = pythonVer + workingDir = "$projectDir/src/test/python" + }.let { + tasks.named("testPython") { dependsOn(it) } + } +} + + +tasks.register("testIntegration") { + group = "verification" +}.let { + tasks.named("check") { dependsOn(it) } } // This should match the version discovery logic in ci.yml. @@ -151,7 +175,7 @@ val INTEGRATION_DIR = "$projectDir/src/test/integration" for (f in file("$INTEGRATION_DIR/data/base").listFiles()!!) { val version = f.name if (version.contains(".")) { - val task = tasks.register("testIntegration-$version") { + tasks.register("testIntegration-$version") { pythonVersion = Common.DEFAULT_PYTHON_VERSION if (System.getenv("CHAQUOPY_NO_BUILD") == null) { // Used in CI dependsOn(tasks.publish, ":runtime:publish") @@ -159,7 +183,8 @@ for (f in file("$INTEGRATION_DIR/data/base").listFiles()!!) { workingDir = INTEGRATION_DIR environment["CHAQUOPY_AGP_VERSION"] = version environment["ANDROID_HOME"] = BuildCommon.androidHome(project) + }.let { + tasks.named("testIntegration") { dependsOn(it) } } - tasks.named("testIntegration") { dependsOn(task) } } } diff --git a/product/gradle-plugin/src/main/python/chaquopy/static_proxy.py b/product/gradle-plugin/src/main/python/chaquopy/static_proxy.py index 908c871b9e..56c995404c 100644 --- a/product/gradle-plugin/src/main/python/chaquopy/static_proxy.py +++ b/product/gradle-plugin/src/main/python/chaquopy/static_proxy.py @@ -15,7 +15,6 @@ from os.path import exists, isdir, isfile import sys import tokenize -import warnings import attr from attr.validators import instance_of, optional @@ -41,10 +40,6 @@ def unwrap_if_primitive(name): def main(): - # TODO: remove once our minimum buildPython version is Python 3.8. - for message in [r".*use ast.Constant instead", r".*use value instead"]: - warnings.filterwarnings("ignore", message, DeprecationWarning) - args = parse_args() try: @@ -296,11 +291,7 @@ def has_kwargs(self, call): return any(kw.arg is None for kw in call.keywords) def evaluate(self, expr): - if isinstance(expr, ast.Num): - return expr.n - elif isinstance(expr, ast.Str): - return expr.s - elif isinstance(expr, ast.NameConstant): # True, False, None + if isinstance(expr, ast.Constant): return expr.value elif isinstance(expr, ast.Name): return self.resolve(expr) diff --git a/product/gradle-plugin/src/main/python/chaquopy/util.py b/product/gradle-plugin/src/main/python/chaquopy/util.py index 84128ed44f..2d00a5e952 100644 --- a/product/gradle-plugin/src/main/python/chaquopy/util.py +++ b/product/gradle-plugin/src/main/python/chaquopy/util.py @@ -2,7 +2,7 @@ def check_build_python(): - MIN_VERSION = (3, 7) + MIN_VERSION = (3, 8) if sys.version_info < MIN_VERSION: print("buildPython must be version {}.{} or later: this is version {}.{}.{}. See " "https://chaquo.com/chaquopy/doc/current/android.html#buildpython." diff --git a/product/gradle-plugin/src/test/integration/data/PythonReqs/buildpython/app/build.gradle b/product/gradle-plugin/src/test/integration/data/PythonReqs/buildpython/app/build.gradle index c98e5238e4..743feb57b7 100644 --- a/product/gradle-plugin/src/test/integration/data/PythonReqs/buildpython/app/build.gradle +++ b/product/gradle-plugin/src/test/integration/data/PythonReqs/buildpython/app/build.gradle @@ -20,6 +20,9 @@ android { install "no_binary==1.0" // sdist install "six==1.16.0" // PyPI download } + pyc { + pip false + } } ndk { abiFilters "x86" diff --git a/product/gradle-plugin/src/test/integration/data/StaticProxy/buildpython/app/build.gradle b/product/gradle-plugin/src/test/integration/data/StaticProxy/buildpython/app/build.gradle index 36787f32fa..3c5fd9d2fa 100644 --- a/product/gradle-plugin/src/test/integration/data/StaticProxy/buildpython/app/build.gradle +++ b/product/gradle-plugin/src/test/integration/data/StaticProxy/buildpython/app/build.gradle @@ -15,6 +15,9 @@ android { buildPython (System.getProperty("os.name").startsWith("Windows") ? "py -$version" : "python$version") staticProxy "chaquopy_test.a" + pyc { + src false + } } ndk { abiFilters "x86" diff --git a/product/gradle-plugin/src/test/integration/test_gradle_plugin.py b/product/gradle-plugin/src/test/integration/test_gradle_plugin.py index 9588c7c052..d0cca500fa 100644 --- a/product/gradle-plugin/src/test/integration/test_gradle_plugin.py +++ b/product/gradle-plugin/src/test/integration/test_gradle_plugin.py @@ -66,8 +66,8 @@ def list_versions(mode): BUILD_PYTHON_VERSION = BUILD_PYTHON_VERSION_FULL.rpartition(".")[0] # When updating these, consider also updating .github/actions/setup-python/action.yml. -OLD_BUILD_PYTHON_VERSION = "3.6" -MIN_BUILD_PYTHON_VERSION = "3.7" +OLD_BUILD_PYTHON_VERSION = "3.7" +MIN_BUILD_PYTHON_VERSION = "3.8" MAX_BUILD_PYTHON_VERSION = "3.12" EGG_INFO_SUFFIX = "py" + BUILD_PYTHON_VERSION + ".egg-info" diff --git a/product/gradle-plugin/src/test/python/static_proxy/test_static_proxy.py b/product/gradle-plugin/src/test/python/static_proxy/test_static_proxy.py index 85a8affc4a..48f2d558b1 100644 --- a/product/gradle-plugin/src/test/python/static_proxy/test_static_proxy.py +++ b/product/gradle-plugin/src/test/python/static_proxy/test_static_proxy.py @@ -43,9 +43,10 @@ def test_errors(self): for name in ["empty", "no_proxies", "conditional"]: self.run_json("errors", name, False, name + ".py: no static_proxy classes found") - self.run_json("errors", "syntax", False, "syntax.py:3:7: invalid syntax") + self.run_json("errors", "syntax", False, "syntax.py:3:5: invalid syntax") self.run_json("errors", "syntax_py2", False, - "syntax_py2.py:1:13: Missing parentheses in call to 'print'") + f"syntax_py2.py:1:{7 if sys.version_info < (3, 10) else 1}" + f": Missing parentheses in call to 'print'") for name in ["starargs", "kwargs"]: self.run_json("errors", name, False, @@ -129,6 +130,9 @@ def run_json(self, path, modules, succeed=True, expected=None, **kwargs): if status == 0: if not succeed: self.dump_run("run unexpectedly succeeded", stdout, stderr) + if stderr: + # Probably a DeprecationWarning or similar. + self.dump_run("succeeded but wrote to stderr", stdout, stderr) try: actual = json.loads(stdout) except ValueError: @@ -150,7 +154,7 @@ def dump_run(self, msg, stdout, stderr): def assertInLong(self, a, b, re=False): try: if re: - self.assertRegexpMatches(b, a) + self.assertRegex(b, a) else: self.assertIn(a, b) except AssertionError: diff --git a/product/gradle-plugin/src/test/python/static_proxy/test_utils.py b/product/gradle-plugin/src/test/python/static_proxy/test_utils.py index ae76309126..a94a64536f 100644 --- a/product/gradle-plugin/src/test/python/static_proxy/test_utils.py +++ b/product/gradle-plugin/src/test/python/static_proxy/test_utils.py @@ -9,7 +9,6 @@ def setUp(self): self.cw = catch_warnings() self.cw.__enter__() filterwarnings("error") - filterwarnings("ignore", r"Please use assert\w+ instead") def tearDown(self): self.cw.__exit__(None, None, None) diff --git a/product/runtime/docs/sphinx/android.rst b/product/runtime/docs/sphinx/android.rst index f4a94c6472..0bc8781732 100644 --- a/product/runtime/docs/sphinx/android.rst +++ b/product/runtime/docs/sphinx/android.rst @@ -167,8 +167,8 @@ example, here's how to create flavors for different :ref:`Python versions buildPython ----------- -Some features require Python 3.7 or later to be available on the build machine. These features -are indicated by a note in their documentation sections. +Some features require Python 3.8 or later to be available on the build machine. These +features are indicated by a note in their documentation sections. By default, Chaquopy will try to find Python on the PATH with the standard command for your operating system, first with a matching minor version, and then with a matching major version.