diff --git a/.gitignore b/.gitignore index aab3981f52f..c24356df575 100644 --- a/.gitignore +++ b/.gitignore @@ -102,6 +102,7 @@ MaintainersFormat.txt ModulesMaintainers.txt Nits.txt Pylint.txt +PythonCompat.txt Ruff.txt SphinxLint.txt SysbuildKconfig.txt diff --git a/scripts/ci/check_compliance.py b/scripts/ci/check_compliance.py index cd97ea96717..88bb50f871a 100755 --- a/scripts/ci/check_compliance.py +++ b/scripts/ci/check_compliance.py @@ -1815,6 +1815,66 @@ class Ruff(ComplianceTest): desc = f"Run 'ruff format {file}'" self.fmtd_failure("error", "Python format error", file, desc=desc) +class PythonCompatCheck(ComplianceTest): + """ + Python Compatibility Check + """ + name = "PythonCompat" + doc = "Check that Python files are compatible with Zephyr minimum supported Python version." + + MAX_VERSION = (3, 10) + MAX_VERSION_STR = f"{MAX_VERSION[0]}.{MAX_VERSION[1]}" + + def run(self): + py_files = [f for f in get_files(filter="d") if f.endswith(".py")] + if not py_files: + return + cmd = ["vermin", "-f", "parsable", "--violations", + f"-t={self.MAX_VERSION_STR}", "--no-make-paths-absolute"] + py_files + try: + result = subprocess.run(cmd, + check=False, + capture_output=True, + cwd=GIT_TOP) + except Exception as ex: + self.error(f"Failed to run vermin: {ex}") + output = result.stdout.decode("utf-8") + failed = False + for line in output.splitlines(): + parts = line.split(":") + if len(parts) < 6: + continue + filename, line_number, column, _, py3ver, feature = parts[:6] + if not line_number: + # Ignore all file-level messages + continue + + desc = None + if py3ver.startswith('!'): + desc = f"{feature} is known to be incompatible with Python 3." + elif py3ver.startswith('~'): + # "no known reason it won't work", just skip + continue + else: + major, minor = map(int, py3ver.split(".")[:2]) + if (major, minor) > self.MAX_VERSION: + desc = f"{feature} requires Python {major}.{minor}, which is higher than " \ + f"Zephyr's minimum supported Python version ({self.MAX_VERSION_STR})." + + if desc is not None: + self.fmtd_failure( + "error", + "PythonCompat", + filename, + line=int(line_number), + col=int(column) if column else None, + desc=desc, + ) + failed = True + if failed: + self.failure("Some Python files use features that are not compatible with Python " \ + f"{self.MAX_VERSION_STR}.") + class TextEncoding(ComplianceTest): """ diff --git a/scripts/requirements-actions.in b/scripts/requirements-actions.in index bf4a50e2aa2..2094546f423 100644 --- a/scripts/requirements-actions.in +++ b/scripts/requirements-actions.in @@ -35,6 +35,7 @@ tabulate tomli>=1.1.0 tox unidiff +vermin west>=0.14.0 xlsxwriter yamllint diff --git a/scripts/requirements-actions.txt b/scripts/requirements-actions.txt index b1997b68439..065d5992399 100644 --- a/scripts/requirements-actions.txt +++ b/scripts/requirements-actions.txt @@ -1235,6 +1235,10 @@ urllib3==2.4.0 \ # elastic-transport # pygithub # requests +vermin==1.6.0 \ + --hash=sha256:6266ca02f55d1c2aa189a610017c132eb2d1934f09e72a955b1eb3820ee6d4ef \ + --hash=sha256:f1fa9ee40f59983dc40e0477eb2b1fa8061a3df4c3b2bcf349add462a5610efb + # via -r requirements-actions.in virtualenv==20.31.2 \ --hash=sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11 \ --hash=sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af diff --git a/scripts/requirements-compliance.txt b/scripts/requirements-compliance.txt index a7134e7ff00..19bdcb3071f 100644 --- a/scripts/requirements-compliance.txt +++ b/scripts/requirements-compliance.txt @@ -13,5 +13,6 @@ python-magic; sys_platform != "win32" ruff==0.11.11 sphinx-lint unidiff +vermin yamllint # zephyr-keep-sorted-stop