From 5cc130c38ccd913360bd9cf86441faee48c16694 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Wed, 15 Nov 2023 14:42:18 +0000 Subject: [PATCH] Add support for buildPython version 3.12 (closes #931) --- .github/workflows/ci.yml | 6 +- .../com/chaquo/python/internal/Common.java | 12 + product/gradle-plugin/README.md | 4 +- product/gradle-plugin/build.gradle.kts | 3 +- .../src/main/kotlin/PythonPlugin.kt | 4 +- .../src/main/kotlin/PythonTasks.kt | 91 +++-- .../src/main/python/chaquopy/pip_install.py | 3 - .../src/main/python/chaquopy_monkey.py | 22 +- .../pip/_internal/utils/setuptools_build.py | 6 - .../pip/_vendor/pkg_resources/__init__.py | 6 +- .../src/main/python/pip/_vendor/six.py | 42 ++- .../pip/_vendor/urllib3/packages/six.py | 313 +++++++++--------- .../python/pip/_vendor/urllib3/util/ssl_.py | 6 +- .../PythonReqs/buildpython/app/build.gradle | 4 +- .../PythonReqs/pep517_hatch/app/build.gradle | 22 ++ .../pep517_hatch/app/pep517_hatch/hatch1.py | 0 .../app/pep517_hatch/pyproject.toml | 10 + .../test/integration/test_gradle_plugin.py | 14 +- product/runtime/build.gradle | 27 +- 19 files changed, 349 insertions(+), 246 deletions(-) create mode 100644 product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/build.gradle create mode 100644 product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/hatch1.py create mode 100644 product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/pyproject.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5676ce86db..a4699404766 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -161,11 +161,13 @@ jobs: - uses: ./.github/actions/setup-python id: setup-python with: - # This should match OLD_BUILD_PYTHON_VERSION and MIN_BUILD_PYTHON_VERSION - # in test_gradle_plugin. + # This should include OLD_BUILD_PYTHON_VERSION, MIN_BUILD_PYTHON_VERSION and + # MAX_BUILD_PYTHON_VERSION from test_gradle_plugin, if they're not already + # returned by `list-versions.py --minor`. extra-versions: | 3.6 3.7 + 3.12 - name: Download Maven repository uses: actions/download-artifact@v3.0.2 diff --git a/product/buildSrc/src/main/java/com/chaquo/python/internal/Common.java b/product/buildSrc/src/main/java/com/chaquo/python/internal/Common.java index 316f596386d..168b0931169 100644 --- a/product/buildSrc/src/main/java/com/chaquo/python/internal/Common.java +++ b/product/buildSrc/src/main/java/com/chaquo/python/internal/Common.java @@ -22,6 +22,7 @@ public class Common { PYTHON_VERSIONS.put("3.9.13", "1"); PYTHON_VERSIONS.put("3.10.6", "1"); PYTHON_VERSIONS.put("3.11.0", "2"); + // TODO: once we add 3.12, remove it from extra-versions in ci.yml } public static List PYTHON_VERSIONS_SHORT = new ArrayList<>(); @@ -81,6 +82,17 @@ public static String assetZip(String type, String abi) { public static final String ASSET_BUILD_JSON = "build.json"; public static final String ASSET_CACERT = "cacert.pem"; + public static String osName() { + String property = System.getProperty("os.name"); + String[] knownNames = new String[] {"linux", "mac", "windows"}; + for (String name : knownNames) { + if (property.toLowerCase(Locale.ENGLISH).startsWith(name)) { + return name; + } + } + throw new RuntimeException("unknown os.name: " + property); + } + public static String findExecutable(String name) throws FileNotFoundException { File file = new File(name); if (file.isAbsolute()) { diff --git a/product/gradle-plugin/README.md b/product/gradle-plugin/README.md index 26c289f89fa..0d7c178281a 100644 --- a/product/gradle-plugin/README.md +++ b/product/gradle-plugin/README.md @@ -103,8 +103,8 @@ do the following: Check out the upstream-pip branch. -Delete the relevant directories in src/main/python, including the .dist-info directory. Note -that pkg_resources is part of setuptools. +Delete the package from src/main/python, including the .dist-info directory. Note that +setuptools includes some files outside of its main directory. Download the wheel of the new version, and unpack it into src/main/python. diff --git a/product/gradle-plugin/build.gradle.kts b/product/gradle-plugin/build.gradle.kts index b9774501716..b6c25c29f70 100644 --- a/product/gradle-plugin/build.gradle.kts +++ b/product/gradle-plugin/build.gradle.kts @@ -1,6 +1,7 @@ import com.chaquo.python.internal.BuildCommon import com.chaquo.python.internal.Common import com.chaquo.python.internal.Common.findExecutable +import com.chaquo.python.internal.Common.osName plugins { `java-gradle-plugin` @@ -83,7 +84,7 @@ abstract class TestPythonTask : DefaultTask() { pb.directory(File(workingDir)) pb.environment().putAll(environment) - command += if (System.getProperty("os.name").toLowerCase().contains("windows")) { + command += if (osName() == "windows") { listOf(findExecutable("py"), "-$pythonVersion") } else { listOf(findExecutable("python$pythonVersion")) diff --git a/product/gradle-plugin/src/main/kotlin/PythonPlugin.kt b/product/gradle-plugin/src/main/kotlin/PythonPlugin.kt index 69f37c84052..51b9b48bbc0 100644 --- a/product/gradle-plugin/src/main/kotlin/PythonPlugin.kt +++ b/product/gradle-plugin/src/main/kotlin/PythonPlugin.kt @@ -135,12 +135,12 @@ class PythonPlugin : Plugin { "python", extension.defaultConfig) android.productFlavors.all { - val python = extension.productFlavors.maybeCreate(name) + val python = extension.productFlavors.maybeCreate(name) // New DSL (this as ExtensionAware).extensions.add("python", python) // Old DSL } android.sourceSets.all { - val dirSet = extension.sourceSets.maybeCreate(name) + val dirSet = extension.sourceSets.maybeCreate(name) // New DSL (this as ExtensionAware).extensions.add("python", dirSet) // Old DSL } } diff --git a/product/gradle-plugin/src/main/kotlin/PythonTasks.kt b/product/gradle-plugin/src/main/kotlin/PythonTasks.kt index 31d1c77487b..be1f10ad5fa 100644 --- a/product/gradle-plugin/src/main/kotlin/PythonTasks.kt +++ b/product/gradle-plugin/src/main/kotlin/PythonTasks.kt @@ -3,6 +3,7 @@ package com.chaquo.python import com.android.build.api.variant.* import com.chaquo.python.internal.* import com.chaquo.python.internal.Common.assetZip +import com.chaquo.python.internal.Common.osName import org.apache.commons.compress.archivers.zip.* import org.gradle.api.* import org.gradle.api.artifacts.* @@ -24,9 +25,7 @@ internal class TaskBuilder( val abis: List ) { val project = plugin.project - lateinit var buildPython: List - - lateinit var buildPackagesTask: Provider + lateinit var buildPackagesTask: Provider lateinit var srcTask: Provider lateinit var reqsTask: Provider @@ -56,33 +55,82 @@ internal class TaskBuilder( } } - fun createBuildPackagesTask(): Provider { + fun createBuildPackagesTask(): Provider { val taskName = "extractPythonBuildPackages" try { - return project.tasks.named(taskName) + return project.tasks.named(taskName) } catch (e: UnknownDomainObjectException) { - return project.tasks.register(taskName) { + return project.tasks.register(taskName) { + var bp: List? + try { + bp = findBuildPython() + } catch (e: BuildPythonException) { + bp = null + exception = e + } + inputs.property("buildPython", bp).optional(true) + // Keep the path short to avoid the the Windows 260-character limit. - outputFiles = project.fileTree(plugin.buildSubdir("bp")) { + outputFiles = project.fileTree(plugin.buildSubdir("env")) { exclude("**/__pycache__") } - doLast { - val zipPath = plugin.extractResource( - "gradle/build-packages.zip", plugin.buildSubdir()) - project.copy { - from(project.zipTree(zipPath)) - into(outputDir) + + if (bp != null) { + doLast { + project.exec { + commandLine(bp) + args("-m", "venv", "--without-pip", outputDir) + } + + val zipPath = plugin.extractResource( + "gradle/build-packages.zip", plugin.buildSubdir()) + project.copy { + from(project.zipTree(zipPath)) + into(sitePackages) + } + project.delete(zipPath) } - project.delete(zipPath) } } } } + open class BuildPackagesTask : OutputDirTask() { + @get:Internal + lateinit var exception: Exception + + @get:Internal + val pythonExecutable by lazy { + if (::exception.isInitialized) { + throw exception + } else if (osName() == "windows") { + outputDir.resolve("Scripts/python.exe") + } else { + outputDir.resolve("bin/python") + } + } + + @get:Internal + val sitePackages by lazy { + if (osName() == "windows") { + outputDir.resolve("Lib/site-packages") + } else { + val libDir = outputDir.resolve("lib") + val pythonDirs = libDir.listFiles()!!.filter { + it.name.startsWith("python") + } + if (pythonDirs.size != 1) { + throw GradleException( + "found ${pythonDirs.size} python directories in $libDir") + } + pythonDirs[0].resolve("site-packages") + } + } + } + fun createSrcTask() = registerTask("merge", "sources") { inputs.files(buildPackagesTask) - inputs.property("buildPython", python.buildPython).optional(true) inputs.property("pyc", python.pyc.src).optional(true) val dirSets = ArrayList() @@ -149,7 +197,6 @@ internal class TaskBuilder( inputs.files(buildPackagesTask) inputs.property("abis", abis) inputs.property("minApiLevel", variant.minSdkVersion.apiLevel) - inputs.property("buildPython", python.buildPython).optional(true) inputs.property("pip", python.pip) inputs.property("pyc", python.pyc.pip).optional(true) @@ -261,7 +308,6 @@ internal class TaskBuilder( val outputDir = plugin.buildSubdir("proxies", variant) val task = registerTask("generate", "proxies") { inputs.files(buildPackagesTask, reqsTask, srcTask) - inputs.property("buildPython", python.buildPython).optional(true) inputs.property("staticProxy", python.staticProxy) this.outputDir = outputDir @@ -542,16 +588,9 @@ internal class TaskBuilder( } fun execBuildPython(configure: ExecSpec.() -> Unit) { - if (! ::buildPython.isInitialized) { - buildPython = findBuildPython() - } - try { project.exec { - environment("PYTHONPATH", buildPackagesTask.get().outputDir) - commandLine(buildPython) - args("-S") // Avoid interference from site-packages. This is not inherited by - // subprocesses, so it's used again in pip_install.py. + executable(buildPackagesTask.get().pythonExecutable) configure() } } catch (e: ExecException) { @@ -579,7 +618,7 @@ internal class TaskBuilder( } else { val version = python.version!! for (suffix in listOf(version, version.split(".")[0])) { - if (System.getProperty("os.name").startsWith("Windows")) { + if (osName() == "windows") { // See PEP 397. After running the official Windows installer // with default settings, this will be the only Python thing on // the PATH. diff --git a/product/gradle-plugin/src/main/python/chaquopy/pip_install.py b/product/gradle-plugin/src/main/python/chaquopy/pip_install.py index 94bca6806a3..37c856da1f1 100644 --- a/product/gradle-plugin/src/main/python/chaquopy/pip_install.py +++ b/product/gradle-plugin/src/main/python/chaquopy/pip_install.py @@ -111,9 +111,6 @@ def pip_install(self, abi, reqs): # Warning: `pip install --target` is very simple-minded: see # https://github.com/pypa/pip/issues/4625#issuecomment-375977073. cmdline = ([sys.executable, - "-S", # Avoid interference from site-packages. This is not inherited - # by subprocesses, so it's used again in pip (see wheel.py and - # req_install.py). "-m", "pip", "install", "--isolated", # Disables environment variables. "--target", abi_dir, diff --git a/product/gradle-plugin/src/main/python/chaquopy_monkey.py b/product/gradle-plugin/src/main/python/chaquopy_monkey.py index 53e49c21d46..2967347ee39 100644 --- a/product/gradle-plugin/src/main/python/chaquopy_monkey.py +++ b/product/gradle-plugin/src/main/python/chaquopy_monkey.py @@ -1,3 +1,8 @@ +import os +import sys +import types + + # We want to cause a quick and comprehensible failure when a package attempts to build # native code, while still allowing a pure-Python fallback if available. This is tricky, # because different packages have different approaches to pure-Python fallbacks: @@ -30,6 +35,11 @@ # list (e.g. minorminer, lz4), but the error messages from these packages aren't too bad, and # I've never seen one which has a pure-Python fallback. def disable_native(): + disable_native_distutils() + disable_native_environ() + + +def disable_native_distutils(): # Recent versions of setuptools redirect distutils to their own bundled copy, so try # to import that first. try: @@ -37,11 +47,15 @@ def disable_native(): except ImportError: pass + try: + import distutils # noqa: F401 + except ImportError: + # distutils was removed in Python 3.12, so it will only exist if setuptools is + # in the build environment. + return + from distutils import ccompiler from distutils.unixccompiler import UnixCCompiler - import os - import sys - import types ccompiler.get_default_compiler = lambda *args, **kwargs: "disabled" ccompiler.compiler_class["disabled"] = ( @@ -73,6 +87,8 @@ def link(*args, **kwargs): disabled_mod.DisabledCompiler = DisabledCompiler sys.modules[disabled_mod_name] = disabled_mod + +def disable_native_environ(): # Try to disable native builds for packages which don't use the distutils native build # system at all (e.g. uwsgi), or only use it to wrap an external build script (e.g. pynacl). for tool in ["ar", "as", "cc", "cxx", "ld"]: diff --git a/product/gradle-plugin/src/main/python/pip/_internal/utils/setuptools_build.py b/product/gradle-plugin/src/main/python/pip/_internal/utils/setuptools_build.py index c266b5045c4..6d3fba42c38 100644 --- a/product/gradle-plugin/src/main/python/pip/_internal/utils/setuptools_build.py +++ b/product/gradle-plugin/src/main/python/pip/_internal/utils/setuptools_build.py @@ -41,12 +41,6 @@ def make_setuptools_shim_args( if unbuffered_output: args += ["-u"] - # Chaquopy: added '-S' to avoid interference from site-packages. This makes - # non-installable packages fail more quickly and consistently. Also, some packages - # (e.g. Cython) install distutils hooks which can interfere with our attempts to - # disable compilers in chaquopy_monkey. - args.append('-S') - from pip._vendor.packaging import markers chaquopy_monkey = ( "import chaquopy_monkey; chaquopy_monkey.disable_native()" diff --git a/product/gradle-plugin/src/main/python/pip/_vendor/pkg_resources/__init__.py b/product/gradle-plugin/src/main/python/pip/_vendor/pkg_resources/__init__.py index a457ff27ef0..e2fa8ab0bad 100644 --- a/product/gradle-plugin/src/main/python/pip/_vendor/pkg_resources/__init__.py +++ b/product/gradle-plugin/src/main/python/pip/_vendor/pkg_resources/__init__.py @@ -2161,7 +2161,8 @@ def resolve_egg_link(path): return next(dist_groups, ()) -register_finder(pkgutil.ImpImporter, find_on_path) +# Chaquopy: ImpImporter was removed in Python 3.12. +# register_finder(pkgutil.ImpImporter, find_on_path) if hasattr(importlib_machinery, 'FileFinder'): register_finder(importlib_machinery.FileFinder, find_on_path) @@ -2312,7 +2313,8 @@ def file_ns_handler(importer, path_item, packageName, module): return subpath -register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) +# Chaquopy: ImpImporter was removed in Python 3.12. +# register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) register_namespace_handler(zipimport.zipimporter, file_ns_handler) if hasattr(importlib_machinery, 'FileFinder'): diff --git a/product/gradle-plugin/src/main/python/pip/_vendor/six.py b/product/gradle-plugin/src/main/python/pip/_vendor/six.py index 5fe9f8e141e..aa00ae175c6 100644 --- a/product/gradle-plugin/src/main/python/pip/_vendor/six.py +++ b/product/gradle-plugin/src/main/python/pip/_vendor/six.py @@ -1,3 +1,9 @@ +# Chaquopy: backported from pip 23.3.1 to support Python 3.12. Error was: +# +# File "/Users/msmith/git/chaquo/chaquopy/product/gradle-plugin/build/test/integration/8.1/PythonReqs/buildpython/project/app/build/python/bp/pip/_vendor/pkg_resources/__init__.py", line 58, in +# from pip._vendor.six.moves import urllib, map, filter +# ModuleNotFoundError: No module named 'pip._vendor.six.moves' + # Copyright (c) 2010-2020 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -29,7 +35,7 @@ import types __author__ = "Benjamin Peterson " -__version__ = "1.14.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -71,6 +77,11 @@ def __len__(self): MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -186,6 +197,11 @@ def find_module(self, fullname, path=None): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -223,6 +239,12 @@ def get_code(self, fullname): return None get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + _importer = _SixMetaPathImporter(__name__) @@ -890,12 +912,11 @@ def ensure_binary(s, encoding='utf-8', errors='strict'): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) def ensure_str(s, encoding='utf-8', errors='strict'): @@ -909,12 +930,15 @@ def ensure_str(s, encoding='utf-8', errors='strict'): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s diff --git a/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/packages/six.py b/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/packages/six.py index 314424099f6..0b10381a05d 100644 --- a/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/packages/six.py +++ b/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/packages/six.py @@ -1,4 +1,10 @@ -# Copyright (c) 2010-2019 Benjamin Peterson +# Chaquopy: backported from pip 23.3.1 to support Python 3.12. Error was: +# +# File "/Users/msmith/git/chaquo/chaquopy/product/gradle-plugin/build/test/integration/8.1/PythonReqs/buildpython/project/app/build/python/env/lib/python3.12/site-packages/pip/_vendor/urllib3/exceptions.py", line 2, in +# from .packages.six.moves.http_client import IncompleteRead as httplib_IncompleteRead +# ModuleNotFoundError: No module named 'pip._vendor.urllib3.packages.six.moves' + +# Copyright (c) 2010-2020 Benjamin Peterson # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -29,7 +35,7 @@ import types __author__ = "Benjamin Peterson " -__version__ = "1.12.0" +__version__ = "1.16.0" # Useful for very coarse version differentiation. @@ -38,15 +44,15 @@ PY34 = sys.version_info[0:2] >= (3, 4) if PY3: - string_types = (str,) - integer_types = (int,) - class_types = (type,) + string_types = str, + integer_types = int, + class_types = type, text_type = str binary_type = bytes MAXSIZE = sys.maxsize else: - string_types = (basestring,) + string_types = basestring, integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode @@ -58,9 +64,9 @@ else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): + def __len__(self): return 1 << 31 - try: len(X()) except OverflowError: @@ -71,6 +77,11 @@ def __len__(self): MAXSIZE = int((1 << 63) - 1) del X +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + def _add_doc(func, doc): """Add documentation to a function.""" @@ -84,6 +95,7 @@ def _import_module(name): class _LazyDescr(object): + def __init__(self, name): self.name = name @@ -100,6 +112,7 @@ def __get__(self, obj, tp): class MovedModule(_LazyDescr): + def __init__(self, name, old, new=None): super(MovedModule, self).__init__(name) if PY3: @@ -120,6 +133,7 @@ def __getattr__(self, attr): class _LazyModule(types.ModuleType): + def __init__(self, name): super(_LazyModule, self).__init__(name) self.__doc__ = self.__class__.__doc__ @@ -134,6 +148,7 @@ def __dir__(self): class MovedAttribute(_LazyDescr): + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): super(MovedAttribute, self).__init__(name) if PY3: @@ -182,6 +197,11 @@ def find_module(self, fullname, path=None): return self return None + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + def __get_module(self, fullname): try: return self.known_modules[fullname] @@ -217,9 +237,13 @@ def get_code(self, fullname): Required, if is_package is implemented""" self.__get_module(fullname) # eventually raises ImportError return None - get_source = get_code # same as get_code + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass _importer = _SixMetaPathImporter(__name__) @@ -227,16 +251,13 @@ def get_code(self, fullname): class _MovedItems(_LazyModule): """Lazy loading of moved objects""" - __path__ = [] # mark as package _moved_attributes = [ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute( - "filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse" - ), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), MovedAttribute("intern", "__builtin__", "sys"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), @@ -244,9 +265,7 @@ class _MovedItems(_LazyModule): MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), MovedAttribute("getoutput", "commands", "subprocess"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute( - "reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload" - ), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), MovedAttribute("reduce", "__builtin__", "functools"), MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), MovedAttribute("StringIO", "StringIO", "io"), @@ -255,14 +274,14 @@ class _MovedItems(_LazyModule): MovedAttribute("UserString", "UserString", "collections"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute( - "zip_longest", "itertools", "itertools", "izip_longest", "zip_longest" - ), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), MovedModule("copyreg", "copy_reg"), MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), @@ -271,9 +290,7 @@ class _MovedItems(_LazyModule): MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule( - "email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart" - ), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), @@ -292,12 +309,15 @@ class _MovedItems(_LazyModule): MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", "tkinter.commondialog"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), MovedModule("tkinter_font", "tkFont", "tkinter.font"), MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), @@ -307,7 +327,9 @@ class _MovedItems(_LazyModule): ] # Add windows specific modules. if sys.platform == "win32": - _moved_attributes += [MovedModule("winreg", "_winreg")] + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) @@ -341,9 +363,7 @@ class Module_six_moves_urllib_parse(_LazyModule): MovedAttribute("quote_plus", "urllib", "urllib.parse"), MovedAttribute("unquote", "urllib", "urllib.parse"), MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute( - "unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes" - ), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), MovedAttribute("urlencode", "urllib", "urllib.parse"), MovedAttribute("splitquery", "urllib", "urllib.parse"), MovedAttribute("splittag", "urllib", "urllib.parse"), @@ -361,11 +381,8 @@ class Module_six_moves_urllib_parse(_LazyModule): Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes -_importer._add_module( - Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", - "moves.urllib.parse", -) +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") class Module_six_moves_urllib_error(_LazyModule): @@ -384,11 +401,8 @@ class Module_six_moves_urllib_error(_LazyModule): Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes -_importer._add_module( - Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", - "moves.urllib.error", -) +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") class Module_six_moves_urllib_request(_LazyModule): @@ -439,11 +453,8 @@ class Module_six_moves_urllib_request(_LazyModule): Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes -_importer._add_module( - Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", - "moves.urllib.request", -) +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") class Module_six_moves_urllib_response(_LazyModule): @@ -463,11 +474,8 @@ class Module_six_moves_urllib_response(_LazyModule): Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes -_importer._add_module( - Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", - "moves.urllib.response", -) +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") class Module_six_moves_urllib_robotparser(_LazyModule): @@ -476,27 +484,21 @@ class Module_six_moves_urllib_robotparser(_LazyModule): _urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser") + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), ] for attr in _urllib_robotparser_moved_attributes: setattr(Module_six_moves_urllib_robotparser, attr.name, attr) del attr -Module_six_moves_urllib_robotparser._moved_attributes = ( - _urllib_robotparser_moved_attributes -) +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes -_importer._add_module( - Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", - "moves.urllib.robotparser", -) +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") class Module_six_moves_urllib(types.ModuleType): """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package parse = _importer._get_module("moves.urllib_parse") error = _importer._get_module("moves.urllib_error") @@ -505,12 +507,10 @@ class Module_six_moves_urllib(types.ModuleType): robotparser = _importer._get_module("moves.urllib_robotparser") def __dir__(self): - return ["parse", "error", "request", "response", "robotparser"] + return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module( - Module_six_moves_urllib(__name__ + ".moves.urllib"), "moves.urllib" -) +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") def add_move(move): @@ -550,24 +550,19 @@ def remove_move(name): try: advance_iterator = next except NameError: - def advance_iterator(it): return it.next() - - next = advance_iterator try: callable = callable except NameError: - def callable(obj): return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) if PY3: - def get_unbound_function(unbound): return unbound @@ -578,7 +573,6 @@ def create_unbound_method(func, cls): Iterator = object else: - def get_unbound_function(unbound): return unbound.im_func @@ -589,13 +583,13 @@ def create_unbound_method(func, cls): return types.MethodType(func, None, cls) class Iterator(object): + def next(self): return type(self).__next__(self) callable = callable -_add_doc( - get_unbound_function, """Get the function out of a possibly unbound function""" -) +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") get_method_function = operator.attrgetter(_meth_func) @@ -607,7 +601,6 @@ def next(self): if PY3: - def iterkeys(d, **kw): return iter(d.keys(**kw)) @@ -626,7 +619,6 @@ def iterlists(d, **kw): viewitems = operator.methodcaller("items") else: - def iterkeys(d, **kw): return d.iterkeys(**kw) @@ -647,30 +639,26 @@ def iterlists(d, **kw): _add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") _add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc( - iterlists, "Return an iterator over the (key, [values]) pairs of a dictionary." -) +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") if PY3: - def b(s): return s.encode("latin-1") def u(s): return s - unichr = chr import struct - int2byte = struct.Struct(">B").pack del struct byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter import io - StringIO = io.StringIO BytesIO = io.BytesIO del io @@ -678,19 +666,18 @@ def u(s): if sys.version_info[1] <= 1: _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" else: _assertRaisesRegex = "assertRaisesRegex" _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" else: - def b(s): return s - # Workaround for standalone backslash def u(s): - return unicode(s.replace(r"\\", r"\\\\"), "unicode_escape") - + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") unichr = unichr int2byte = chr @@ -699,14 +686,13 @@ def byte2int(bs): def indexbytes(buf, i): return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) import StringIO - StringIO = BytesIO = StringIO.StringIO _assertCountEqual = "assertItemsEqual" _assertRaisesRegex = "assertRaisesRegexp" _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") @@ -723,6 +709,10 @@ def assertRegex(self, *args, **kwargs): return getattr(self, _assertRegex)(*args, **kwargs) +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + if PY3: exec_ = getattr(moves.builtins, "exec") @@ -737,9 +727,7 @@ def reraise(tp, value, tb=None): value = None tb = None - else: - def exec_(_code_, _globs_=None, _locs_=None): """Execute code in a namespace.""" if _globs_ is None: @@ -752,45 +740,28 @@ def exec_(_code_, _globs_=None, _locs_=None): _locs_ = _globs_ exec("""exec _code_ in _globs_, _locs_""") - exec_( - """def reraise(tp, value, tb=None): + exec_("""def reraise(tp, value, tb=None): try: raise tp, value, tb finally: tb = None -""" - ) +""") -if sys.version_info[:2] == (3, 2): - exec_( - """def raise_from(value, from_value): +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): try: - if from_value is None: - raise value raise value from from_value finally: value = None -""" - ) -elif sys.version_info[:2] > (3, 2): - exec_( - """def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""" - ) +""") else: - def raise_from(value, from_value): raise value print_ = getattr(moves.builtins, "print", None) if print_ is None: - def print_(*args, **kwargs): """The new-style print function for Python 2.4 and 2.5.""" fp = kwargs.pop("file", sys.stdout) @@ -801,17 +772,14 @@ def write(data): if not isinstance(data, basestring): data = str(data) # If the file has an encoding, encode unicode with it. - if ( - isinstance(fp, file) - and isinstance(data, unicode) - and fp.encoding is not None - ): + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): errors = getattr(fp, "errors", None) if errors is None: errors = "strict" data = data.encode(fp.encoding, errors) fp.write(data) - want_unicode = False sep = kwargs.pop("sep", None) if sep is not None: @@ -847,8 +815,6 @@ def write(data): write(sep) write(arg) write(end) - - if sys.version_info[:2] < (3, 3): _print = print_ @@ -859,23 +825,35 @@ def print_(*args, **kwargs): if flush and fp is not None: fp.flush() - _add_doc(reraise, """Reraise an exception.""") if sys.version_info[0:2] < (3, 4): - - def wraps( - wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES, - ): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ else: wraps = functools.wraps @@ -887,37 +865,43 @@ def with_metaclass(meta, *bases): # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(type): + def __new__(cls, name, this_bases, d): - return meta(name, bases, d) + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) @classmethod def __prepare__(cls, name, this_bases): return meta.__prepare__(name, bases) - - return type.__new__(metaclass, "temporary_class", (), {}) + return type.__new__(metaclass, 'temporary_class', (), {}) def add_metaclass(metaclass): """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): orig_vars = cls.__dict__.copy() - slots = orig_vars.get("__slots__") + slots = orig_vars.get('__slots__') if slots is not None: if isinstance(slots, str): slots = [slots] for slots_var in slots: orig_vars.pop(slots_var) - orig_vars.pop("__dict__", None) - orig_vars.pop("__weakref__", None) - if hasattr(cls, "__qualname__"): - orig_vars["__qualname__"] = cls.__qualname__ + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper -def ensure_binary(s, encoding="utf-8", errors="strict"): +def ensure_binary(s, encoding='utf-8', errors='strict'): """Coerce **s** to six.binary_type. For Python 2: @@ -928,15 +912,14 @@ def ensure_binary(s, encoding="utf-8", errors="strict"): - `str` -> encoded to `bytes` - `bytes` -> `bytes` """ + if isinstance(s, binary_type): + return s if isinstance(s, text_type): return s.encode(encoding, errors) - elif isinstance(s, binary_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) + raise TypeError("not expecting type '%s'" % type(s)) -def ensure_str(s, encoding="utf-8", errors="strict"): +def ensure_str(s, encoding='utf-8', errors='strict'): """Coerce *s* to `str`. For Python 2: @@ -947,16 +930,19 @@ def ensure_str(s, encoding="utf-8", errors="strict"): - `str` -> `str` - `bytes` -> decoded to `str` """ - if not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) + # Optimization: Fast return for the common case. + if type(s) is str: + return s if PY2 and isinstance(s, text_type): - s = s.encode(encoding, errors) + return s.encode(encoding, errors) elif PY3 and isinstance(s, binary_type): - s = s.decode(encoding, errors) + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) return s -def ensure_text(s, encoding="utf-8", errors="strict"): +def ensure_text(s, encoding='utf-8', errors='strict'): """Coerce *s* to six.text_type. For Python 2: @@ -977,20 +963,19 @@ def ensure_text(s, encoding="utf-8", errors="strict"): def python_2_unicode_compatible(klass): """ - A decorator that defines __unicode__ and __str__ methods under Python 2. + A class decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method returning text and apply this decorator to the class. """ if PY2: - if "__str__" not in klass.__dict__: - raise ValueError( - "@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % klass.__name__ - ) + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode("utf-8") + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') return klass @@ -1010,10 +995,8 @@ def python_2_unicode_compatible(klass): # be floating around. Therefore, we can't use isinstance() to check for # the six meta path importer, since the other six instance will have # inserted an importer with different class. - if ( - type(importer).__name__ == "_SixMetaPathImporter" - and importer.name == __name__ - ): + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): del sys.meta_path[i] break del i, importer diff --git a/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/util/ssl_.py b/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/util/ssl_.py index 3f78296f656..6c245cca3a4 100644 --- a/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/util/ssl_.py +++ b/product/gradle-plugin/src/main/python/pip/_vendor/urllib3/util/ssl_.py @@ -38,7 +38,11 @@ def _const_compare_digest_backport(a, b): try: # Test for SSL features import ssl - from ssl import wrap_socket, CERT_REQUIRED + + # Chaquopy: removed wrap_socket, which was removed in Python 3.12, and is only used + # in this file by Python 2 code. + from ssl import CERT_REQUIRED + from ssl import HAS_SNI # Has SNI? except ImportError: pass 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 340521f2688..1a18d1e9fce 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 @@ -15,10 +15,10 @@ android { buildPython (System.getProperty("os.name").startsWith("Windows") ? "py -$version" : "python$version") pip { - options "--no-index" options "--find-links", "${System.getenv('integration_dir')}/packages/dist" - install "apple" // wheel + install "apple==0.0.1" // wheel install "no_binary==1.0" // sdist + install "six==1.16.0" // PyPI download } } ndk { diff --git a/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/build.gradle b/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/build.gradle new file mode 100644 index 00000000000..636fbcab7f6 --- /dev/null +++ b/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'com.android.application' +apply plugin: 'com.chaquo.python' + +android { + namespace "com.chaquo.python.test" + compileSdk 23 + defaultConfig { + applicationId "com.chaquo.python.test" + minSdk 21 + targetSdk 23 + versionCode 1 + versionName "0.0.1" + python { + pip { + install "./pep517_hatch" + } + } + ndk { + abiFilters "x86" + } + } +} diff --git a/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/hatch1.py b/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/hatch1.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/pyproject.toml b/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/pyproject.toml new file mode 100644 index 00000000000..5d24f30a4a0 --- /dev/null +++ b/product/gradle-plugin/src/test/integration/data/PythonReqs/pep517_hatch/app/pep517_hatch/pyproject.toml @@ -0,0 +1,10 @@ +[build-system] +requires = ["hatchling==1.18.0"] +build-backend = "hatchling.build" + +[project] +name = "pep517_hatch" +version = "5.1.7" + +[tool.hatch.build] +include = ["hatch1.py"] \ No newline at end of file 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 a71db4f36b0..de4f791140f 100644 --- a/product/gradle-plugin/src/test/integration/test_gradle_plugin.py +++ b/product/gradle-plugin/src/test/integration/test_gradle_plugin.py @@ -65,11 +65,11 @@ def list_versions(mode): .split()[1]) BUILD_PYTHON_VERSION = BUILD_PYTHON_VERSION_FULL.rpartition(".")[0] -# These should match `extra-versions` in the ci.yml job `test-integration`. +# When updating these, consider also updating extra-versions in ci.yml. OLD_BUILD_PYTHON_VERSION = "3.6" MIN_BUILD_PYTHON_VERSION = "3.7" +MAX_BUILD_PYTHON_VERSION = "3.12" -MAX_BUILD_PYTHON_VERSION = "3.11" EGG_INFO_SUFFIX = "py" + BUILD_PYTHON_VERSION + ".egg-info" EGG_INFO_FILES = ["dependency_links.txt", "PKG-INFO", "SOURCES.txt", "top_level.txt"] @@ -772,7 +772,8 @@ def test_buildpython(self): with self.subTest(version=version): self.RunGradle(*layers, env={"buildpython_version": version}, requirements=["apple/__init__.py", - "no_binary_sdist/__init__.py"], + "no_binary_sdist/__init__.py", + "six.py"], pyc=["stdlib"]) run = self.RunGradle(*layers, env={"buildpython_version": OLD_BUILD_PYTHON_VERSION}, @@ -989,6 +990,13 @@ def test_pep517_backend_path(self): self.RunGradle("base", "PythonReqs/pep517", "PythonReqs/pep517_backend_path", **self.PEP517_KWARGS) + # An alternative backend, with setuptools not installed in the build environment. + def test_pep517_hatch(self): + self.RunGradle( + "base", "PythonReqs/pep517_hatch", + dist_versions=[("pep517_hatch", "5.1.7")], + requirements=["hatch1.py"]) + # Make sure we're not affected by a setup.cfg file containing a `prefix` line. def test_cfg_wheel(self): self.RunGradle("base", "PythonReqs/cfg_wheel", requirements=["apple/__init__.py"]) diff --git a/product/runtime/build.gradle b/product/runtime/build.gradle index 5d146081160..dc0cf3cb2b1 100644 --- a/product/runtime/build.gradle +++ b/product/runtime/build.gradle @@ -1,22 +1,11 @@ import com.chaquo.python.internal.BuildCommon; import com.chaquo.python.internal.Common; import static com.chaquo.python.internal.Common.findExecutable; +import static com.chaquo.python.internal.Common.osName; group = "com.chaquo.python.runtime" -def osNameProperty = System.properties['os.name'] -String osName = null -for (name in ["linux", "mac", "windows"]) { - if (osNameProperty.toLowerCase(Locale.ENGLISH).startsWith(name)) { - osName = name - break - } -} -if (osName == null) { - throw new GradleException("unknown os.name: '$osNameProperty'") -} - String hostPythonExe() { return findExecutable("python$Common.DEFAULT_PYTHON_VERSION") } @@ -84,7 +73,7 @@ Common.PYTHON_VERSIONS_SHORT.each { pyVersion -> doLast { exec { workingDir destinationDir - if (osName == "windows") { + if (osName() == "windows") { commandLine findExecutable("py"), "-$pyVersion" } else { commandLine findExecutable("python$pyVersion") @@ -258,13 +247,13 @@ sourceSets.test.java.srcDir(proxyTask) if (abi == "host") { String javaIncludeSubdir - if (osName == "windows") { + if (osName() == "windows") { pyLibSuffix = ".pyd" javaIncludeSubdir = "win32" - } else if (osName == "mac") { + } else if (osName() == "mac") { javaIncludeSubdir = "darwin" } else { - javaIncludeSubdir = osName + javaIncludeSubdir = osName() } args "-DCHAQUOPY_PYTHON_VERSION=$Common.DEFAULT_PYTHON_VERSION" args "-DCHAQUOPY_LIB_DIRS=${hostPythonHome()}/lib" @@ -328,8 +317,8 @@ sourceSets.test.java.srcDir(proxyTask) // Can't use -Djava.library.path because we need something which also works // for Python processes. - def pathVar = (osName == "windows") ? "PATH" - : (osName == "mac") ? "DYLD_LIBRARY_PATH" + def pathVar = (osName() == "windows") ? "PATH" + : (osName() == "mac") ? "DYLD_LIBRARY_PATH" : "LD_LIBRARY_PATH" environment pathVar, (cmakeBuildSubdir + File.pathSeparator + (System.getenv(pathVar) ?: "")) @@ -365,7 +354,7 @@ sourceSets.test.java.srcDir(proxyTask) configure(hostConfig) outputs.upToDateWhen { false } environment "PYTHONHOME", hostPythonHome() - if (osName == "linux") { + if (osName() == "linux") { // libchaquopy_java.so is linked against libpython, but that doesn't add // the libpython symbols to the global scope, which is required in order to // load native modules like ctypes. Loading libpython with