From f82375054ffafadabe66b7907d7e2c77c2ee1fa9 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut <1311787+PierreRaybaut@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:46:45 +0200 Subject: [PATCH] chore: require Python 3.10 for Desktop plugins Refs DataLab-Platform/plugins#5 Assisted-by: GPT-6.1 Sol --- CONTRIBUTING.md | 2 +- README.md | 2 +- tests/test_catalog.py | 4 +++- tools/catalog.py | 2 +- tools/wheels.py | 15 +-------------- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2e92e97..2683cba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,7 +34,7 @@ releases: - version: "1.0.0" filename: datalab_spectrum_tools.py sha256: - requires_python: ">=3.9" + requires_python: ">=3.10" ``` `filename` selects an exact release asset and stays stable between versions; do not add version numbers or hyphens that make the Python module name invalid. `summary`, `license` and a release version are required in the public entry. Literal source identities and versions, when readable, must agree with the entry. Unknown metadata is not evaluated: constructors, dynamic expressions and plugin code are never executed during inspection. A syntactically valid module is not thereby certified as a working or safe plugin. diff --git a/README.md b/README.md index 4e0021c..86b4e00 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Any author may list a plugin by opening a pull request that adds one small YAML - Each plugin is described by `plugins/.yaml`, validated against [schema/plugin-entry.schema.json](schema/plugin-entry.schema.json). - Desktop entries may use `kind: module` with an exact `.py` GitHub release asset, summary, license and version. The source can remain unchanged: available metadata is read statically, and the entry supplies what cannot be determined without execution. The page labels these artifacts **Python file**, and does not include them in the Web filter. -- For each wheel release, the CI downloads the wheel from the GitHub release (or from PyPI), checks its SHA-256 digest against the entry, and inspects it without running its code: pure-Python plugin, `datalab.plugins` (desktop) and/or `datalab.web_plugins` (web) entry points, declared requirements and OSI-approved license. Desktop wheels may also list reviewed dependency wheels, including native wheels, which are verified and mirrored with the plugin. Web requirements must still be provided by its host. Python compatibility is checked per target: a desktop plugin must install on at least one Python version supported by DataLab (3.9 to 3.14), a web plugin on the Python of the Pyodide release used by DataLab-Web (3.12). DataLab resolves the complete dependency graph against the actual installation; publication metadata checks are not a functional certification of every platform. +- For each wheel release, the CI downloads the wheel from the GitHub release (or from PyPI), checks its SHA-256 digest against the entry, and inspects it without running its code: pure-Python plugin, `datalab.plugins` (desktop) and/or `datalab.web_plugins` (web) entry points, declared requirements and OSI-approved license. Desktop wheels may also list reviewed dependency wheels, including native wheels, which are verified and mirrored with the plugin. Web requirements must still be provided by its host. Python compatibility is checked per target: a desktop plugin must install on at least one Python version supported by DataLab (3.10 to 3.14), a web plugin on the Python of the Pyodide release used by DataLab-Web (3.12). DataLab resolves the complete dependency graph against the actual installation; publication metadata checks are not a functional certification of every platform. - When a pull request is merged, the catalog is rebuilt and published: `catalog.json`, copies of wheels in `wheels//` and modules in `modules//`, and a human-readable `index.html`. All artifacts are downloaded again and must still match the reviewed digests. - The page uses content-versioned URLs for its stylesheet and script, so browsers fetch updated assets instead of reusing an older cached version. - Plugins from DataLab-Platform repositories are marked **official**, others **community**. IDs starting with `org.datalab.` are reserved for official plugins. diff --git a/tests/test_catalog.py b/tests/test_catalog.py index b36a9f5..da1ee74 100644 --- a/tests/test_catalog.py +++ b/tests/test_catalog.py @@ -658,9 +658,11 @@ def test_wheels_must_install_on_the_latest_datalab( @pytest.mark.parametrize( ("requires_python", "error"), [ + (">=3.10", None), (">=3.12", None), (">=3.13", r"\(web, Python 3.12\)"), - ("<3.9", r"\(desktop, Python 3.9, 3.10, .*3.14\)"), + ("<3.10", r"\(desktop, Python 3.10, .*3.14\)"), + ("==3.9.*", r"\(desktop, Python 3.10, .*3.14\)"), ], ) def test_targets_are_checked_with_their_python_versions( diff --git a/tools/catalog.py b/tools/catalog.py index 7869a23..7d67474 100644 --- a/tools/catalog.py +++ b/tools/catalog.py @@ -72,7 +72,7 @@ ) #: Python versions of each target: a wheel must install on at least one of them TARGET_PYTHONS = { - "desktop": ("3.9", "3.10", "3.11", "3.12", "3.13", "3.14"), + "desktop": ("3.10", "3.11", "3.12", "3.13", "3.14"), # Python of the Pyodide release used by DataLab-Web (Pyodide 0.26) "web": ("3.12",), } diff --git a/tools/wheels.py b/tools/wheels.py index 0b8f452..609cea8 100644 --- a/tools/wheels.py +++ b/tools/wheels.py @@ -12,12 +12,10 @@ import configparser import hashlib -import importlib.util import io import pathlib import re import sys -import sysconfig import zipfile from collections.abc import Mapping, Sequence from email.parser import BytesParser @@ -109,18 +107,7 @@ def is_compatible_wheel( def _is_stdlib_module(name: str) -> bool: - stdlib_names = getattr(sys, "stdlib_module_names", None) - if stdlib_names is not None: - return name in stdlib_names - # Python 3.9 has no stdlib module list: locate the module instead. - if name in sys.builtin_module_names: - return True - spec = importlib.util.find_spec(name) - if spec is None or not spec.origin: - return False - stdlib_dir = pathlib.Path(sysconfig.get_paths()["stdlib"]).resolve() - origin = pathlib.Path(spec.origin).resolve() - return stdlib_dir in origin.parents and "site-packages" not in origin.parts + return name in sys.stdlib_module_names def _read_bytes(path_or_bytes: str | bytes | bytearray | memoryview) -> bytes: