Files
nuitka-mirror/tests/optimizations/run_all.py
Kay Hayen 0ac329ec01 Scons: Changed error message for using MinGW64 on newer Python to also mention Zig
* Also enhanced reporting error exit reporting through the scons logger to provide
  the exception in the compilation report.

* More error exits in scons should do this.
2026-01-28 13:44:29 +01:00

274 lines
8.3 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
"""Test runners test
This shows that known good existing static optimization works and produces
constant results. This should be used to preserve successful optimization
at compile time against later changes breaking them undetected.
"""
from __future__ import print_function
import os
import sys
# The test runner needs "lxml" itself.
try:
import lxml.etree
except ImportError:
print("Warning, no 'lxml' module installed, cannot do XML based tests.")
sys.exit(0)
# Find nuitka package relative to us.
sys.path.insert(
0,
os.path.normpath(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..")
),
)
# isort:start
from nuitka.tools.testing.Common import (
createSearchMode,
decideFilenameVersionSkip,
getPythonSysPath,
my_print,
setup,
withPythonPathChange,
)
from nuitka.TreeXML import convertXmlToString
from nuitka.utils.Execution import check_call
from nuitka.utils.FileOperations import getFileContents
python_version = setup(suite="optimizations")
search_mode = createSearchMode()
def getKind(node):
result = node.attrib["kind"]
result = result.replace("Statements", "")
result = result.replace("Statement", "")
result = result.replace("Expression", "")
return result
def getRole(node, role):
for child in node:
if child.tag == "role" and child.attrib["name"] == role:
return child
return None
def getSourceRef(filename, node):
return "%s:%s" % (filename, node.attrib["line"])
def isConstantExpression(expression):
kind = getKind(expression)
return kind.startswith("Constant") or kind in (
"ImportModuleHard",
"ImportModuleNameHardExists",
"ImportModuleNameHardMaybeExists",
"ModuleAttributeFileRef",
"ModuleLoaderRef",
)
def checkSequence(filename, statements):
# Complex stuff, pylint: disable=too-many-branches
for statement in statements:
kind = getKind(statement)
# Printing is fine.
if kind == "PrintValue":
(print_arg,) = getRole(statement, "value")
if not isConstantExpression(print_arg):
search_mode.onErrorDetected(
"%s: Error, print of non-constant '%s'."
% (getSourceRef(filename, statement), getKind(print_arg))
)
continue
if kind == "PrintNewline":
continue
# Printing in Python3 is a function call whose return value is ignored.
if kind == "Only":
only_expression = getRole(statement, "expression")[0]
if getKind(only_expression) == "CallNoKeywords":
called_expression = getRole(only_expression, "called")[0]
if getKind(called_expression) == "BuiltinRef":
if called_expression.attrib["builtin_name"] == "print":
continue
if kind == "FrameModule":
checkSequence(filename, getRole(statement, "statements"))
continue
if kind == "FrameFunction":
checkSequence(filename, getRole(statement, "statements"))
continue
if kind.startswith("AssignmentVariable"):
variable_name = statement.attrib["variable_name"]
# Ignore "__spec__" assignment for Python3.4, it is not going
# to be static.
if variable_name == "__spec__":
continue
(assign_source,) = getRole(statement, "source")
if getKind(assign_source) in ("FunctionCreation", "FunctionCreationOld"):
continue
if not isConstantExpression(assign_source):
search_mode.onErrorDetected(
"%s: Error, assignment from non-constant '%s'."
% (getSourceRef(filename, statement), getKind(assign_source))
)
continue
if kind == "AssignmentAttribute":
(assign_source,) = getRole(statement, "expression")
if getKind(assign_source) == "ModuleAttributeSpecRef":
continue
search_mode.onErrorDetected(
"Error, attribute assignment to '%s'." % getKind(assign_source)
)
if kind in ("ReturnNone", "ReturnConstant"):
continue
print(convertXmlToString(statement))
search_mode.onErrorDetected(
"Error, non-print statement of unknown kind '%s'." % kind
)
def main():
for filename in sorted(os.listdir(".")):
if not filename.endswith(".py") or filename.startswith("run_"):
continue
if not decideFilenameVersionSkip(filename):
continue
active = search_mode.consider(dirname=None, filename=filename)
if active:
my_print("Consider", filename, end=" ")
xml_filename = filename.replace(".py", ".xml")
command = [
os.environ["PYTHON"],
os.path.abspath(os.path.join("..", "..", "bin", "nuitka")),
"--xml=%s" % xml_filename,
"--quiet",
"--mode=module",
"--nofollow-imports",
"--generate-c-only",
"--no-progressbar",
filename,
]
if search_mode.isCoverage():
# To avoid re-execution, which is not acceptable to coverage.
if "PYTHONHASHSEED" not in os.environ:
os.environ["PYTHONHASHSEED"] = "0"
command.insert(2, "--must-not-re-execute")
# spell-checker: ignore --rcfile
command = (
command[0:1]
+ ["-S", "-m", "coverage", "run", "--rcfile", os.devnull, "-a"]
+ command[1:]
)
# Coverage modules hates Nuitka to re-execute, and so we must avoid
# that.
python_path = getPythonSysPath()
else:
python_path = None
with withPythonPathChange(python_path):
check_call(command)
# Parse the result into XML and check it
result = getFileContents(xml_filename, mode="rb")
try:
root = lxml.etree.fromstring(result)
except lxml.etree.XMLSyntaxError:
my_print("Problematic XML output:")
my_print(result)
raise
module_body = root[0]
module_statements_sequence = module_body[0]
assert len(module_statements_sequence) == 1
module_statements = next(iter(module_statements_sequence))
try:
checkSequence(filename, module_statements)
for function in root.xpath('role[@name="functions"]/node'):
(function_body,) = function.xpath('role[@name="body"]')
function_statements_sequence = function_body[0]
assert len(function_statements_sequence) == 1
function_statements = next(iter(function_statements_sequence))
checkSequence(filename, function_statements)
except SystemExit:
my_print("Optimization result:")
my_print(result, style="test-debug")
my_print("FAIL.", style="red")
raise
my_print("OK.")
search_mode.finish()
if __name__ == "__main__":
main()
# Python test originally created or extracted from other peoples work. The
# parts from me are licensed as below. It is at least Free Software where
# it's copied from other people. In these cases, that will normally be
# indicated.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.