Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions ci.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@
PIP_EXTRA_INDEX_URL: "",
WATCHDOG_GIT: "",
COMPLIANCE_GIT: "",
BISECT_EMAIL_SMTP_SERVER: "",
BISECT_EMAIL_FROM: "",
npm_config_registry: "",
RODINIA_DATASET_ZIP: "",
GRAALPY_GRAALOS_TOOLCHAIN_URL: "",
Expand Down
3 changes: 0 additions & 3 deletions ci/python-bench.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,6 @@
},
environment +: environment(self.os, self.arch) + {
BISECT_BENCHMARK_CONFIG: "bisect-benchmark.ini",
BISECT_EMAIL_SMTP_SERVER: $.overlay_imports.BISECT_EMAIL_SMTP_SERVER,
BISECT_EMAIL_TO_PATTERN: ".*@oracle.com",
BISECT_EMAIL_FROM: $.overlay_imports.BISECT_EMAIL_FROM,
ENABLE_POLYBENCH_HPC: "yes",
POLYBENCH_HPC_EXTRA_HEADERS: "/cm/shared/apps/papi/papi-5.5.1/include",
POLYBENCH_HPC_PAPI_LIB_DIR: "/cm/shared/apps/papi/papi-5.5.1/lib",
Expand Down
3 changes: 0 additions & 3 deletions ci/python-gate.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,6 @@
MX_OUTPUT_ROOT_INCLUDES_CONFIG: "false", // this is important so we can build things on JDK-latest and run them on older JDKs
CI: "true",
BISECT_BENCHMARK_CONFIG: "bisect-benchmark.ini",
BISECT_EMAIL_FROM: $.overlay_imports.BISECT_EMAIL_FROM,
BISECT_EMAIL_SMTP_SERVER: $.overlay_imports.BISECT_EMAIL_SMTP_SERVER,
BISECT_EMAIL_TO_PATTERN: ".*@oracle.com",
TRUFFLE_STRICT_OPTION_DEPRECATION: "true",
npm_config_registry: $.overlay_imports.npm_config_registry,
CFLAGS: "-ggdb",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1190,7 +1190,7 @@ private void evalNonInteractive(Context context, ConsoleHandler consoleHandler)
src = Source.newBuilder(getLanguageId(), commandString, "<string>").build();
} else {
// the path is passed through a context option, may be empty when running from stdin
src = Source.newBuilder(getLanguageId(), "__graalpython__.run_path()", "<internal>").internal(true).build();
src = Source.newBuilder(getLanguageId(), "__graalpython__.run_path()", "<internal>").internal(true).option("python.NoPythonFrame", "true").build();
}
context.eval(src);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,54 @@ def test_code():
assert code.co_name == test_code.__code__.co_name


def test_getframemodulename():
assert sys._getframemodulename() == __name__
assert sys._getframemodulename(0) == __name__
assert sys._getframemodulename(-1) == __name__
assert sys._getframemodulename(10**6) is None

namespace = {"sys": sys, "__name__": "test_getframemodulename_custom"}
exec("def get_module_name(depth=0): return sys._getframemodulename(depth)", namespace)
assert namespace["get_module_name"]() == "test_getframemodulename_custom"
assert namespace["get_module_name"](1) == __name__


def test_getframemodulename_uses_function_module():
def get_module_name():
return sys._getframemodulename()

old_module = get_module_name.__module__
try:
get_module_name.__module__ = "test_getframemodulename_function_module"
assert get_module_name() == "test_getframemodulename_function_module"
finally:
get_module_name.__module__ = old_module


def test_getframemodulename_missing_name():
namespace = {"sys": sys}
exec("def get_module_name(): return sys._getframemodulename()", namespace)
assert namespace["get_module_name"]() is None


def test_getframemodulename_non_string_name():
namespace = {"sys": sys, "__name__": 42}
exec("def get_module_name(): return sys._getframemodulename()", namespace)
assert namespace["get_module_name"]() == 42


def test_getframemodulename_audit():
seen = []

def hook(event, args):
if event == "sys._getframemodulename":
seen.append(args)

sys.addaudithook(hook)
assert sys._getframemodulename() == __name__
assert seen[-1] == (0,)


def test_builtins():
assert print == sys._getframe().f_builtins["print"]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ test.test_sys.SysModuleTest.test_exit @ darwin-arm64,linux-aarch64,linux-aarch64
test.test_sys.SysModuleTest.test_getdefaultencoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_sys.SysModuleTest.test_getfilesystemencoding @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_sys.SysModuleTest.test_getframe @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_sys.SysModuleTest.test_getframemodulename @ linux-x86_64
test.test_sys.SysModuleTest.test_getrecursionlimit @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
test.test_sys.SysModuleTest.test_getwindowsversion @ win32-AMD64,win32-AMD64-github
test.test_sys.SysModuleTest.test_implementation @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
import static com.oracle.graal.python.nodes.ErrorMessages.WARN_IGNORE_UNIMPORTABLE_BREAKPOINT_S;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__;
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SIZEOF__;
import static com.oracle.graal.python.nodes.StringLiterals.J_NEWLINE;
import static com.oracle.graal.python.nodes.StringLiterals.T_BACKSLASHREPLACE;
Expand Down Expand Up @@ -161,6 +162,7 @@
import com.oracle.graal.python.builtins.Python3Core;
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.builtins.modules.SysModuleBuiltinsClinicProviders.GetFrameModuleNameNodeClinicProviderGen;
import com.oracle.graal.python.builtins.modules.SysModuleBuiltinsClinicProviders.GetFrameNodeClinicProviderGen;
import com.oracle.graal.python.builtins.modules.SysModuleBuiltinsClinicProviders.SetDlopenFlagsClinicProviderGen;
import com.oracle.graal.python.builtins.modules.io.BufferedReaderBuiltins;
Expand Down Expand Up @@ -203,6 +205,7 @@
import com.oracle.graal.python.builtins.objects.tuple.StructSequence;
import com.oracle.graal.python.builtins.objects.tuple.TupleBuiltins;
import com.oracle.graal.python.lib.OsEnvironGetNode;
import com.oracle.graal.python.lib.PyDictGetItem;
import com.oracle.graal.python.lib.PyExceptionGroupInstanceCheckNode;
import com.oracle.graal.python.lib.PyExceptionInstanceCheckNode;
import com.oracle.graal.python.lib.PyFloatAsDoubleNode;
Expand Down Expand Up @@ -897,6 +900,46 @@ static PFrame counted(VirtualFrame frame, int depth,
}
}

@Builtin(name = "_getframemodulename", parameterNames = "depth", minNumOfPositionalArgs = 0, needsFrame = true, callerFlags = CallerFlags.NEEDS_PFRAME)
@ArgumentClinic(name = "depth", defaultValue = "0", conversion = ClinicConversion.Int)
@GenerateNodeFactory
public abstract static class GetFrameModuleNameNode extends PythonUnaryClinicBuiltinNode {
private static final TruffleString T_SYS_GET_FRAME_MODULE_NAME = tsLiteral("sys._getframemodulename");

@Override
protected ArgumentClinicProvider getArgumentClinic() {
return GetFrameModuleNameNodeClinicProviderGen.INSTANCE;
}

@Specialization
static Object counted(VirtualFrame frame, int depth,
@Bind Node inliningTarget,
@Cached AuditNode auditNode,
@Cached ReadFrameNode readFrameNode,
@Cached PyObjectLookupAttr lookupAttrNode,
@Cached PyDictGetItem getItem) {
auditNode.audit(frame, inliningTarget, T_SYS_GET_FRAME_MODULE_NAME, depth);
int frameDepth = Math.max(depth, 0);
PFrame pFrame = readFrameNode.getFrameForReference(frame, PArguments.getCurrentFrameInfo(frame), ReadFrameNode.AllPythonFramesSelector.INSTANCE, frameDepth, 0);
if (pFrame == null) {
return PNone.NONE;
}

if (pFrame.getFunction() != null) {
Object moduleName = lookupAttrNode.execute(frame, inliningTarget, pFrame.getFunction(), T___MODULE__);
return moduleName != PNone.NO_VALUE ? moduleName : PNone.NONE;
}
/*
* Unlike CPython, we don't construct function objects for modules, so we have to try the function globals.
*/
if (pFrame.getGlobals() instanceof PDict globals) {
Object moduleName = getItem.execute(frame, inliningTarget, globals, T___NAME__);
return moduleName != null ? moduleName : PNone.NONE;
}
return PNone.NONE;
}
}

@Builtin(name = "_current_frames")
@GenerateNodeFactory
abstract static class CurrentFrames extends PythonBuiltinNode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ public RootCallTarget getTarget() {
return getCode().getRootCallTarget();
}

public PFunction getFunction() {
return function;
}

public PCode getCode() {
if (code == null && function != null) {
code = function.getCode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.oracle.graal.python.runtime.IndirectCallData;
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonContext;
import com.oracle.graal.python.runtime.PythonSourceOptions;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.RootCallTarget;
Expand All @@ -86,6 +87,7 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.source.Source;

@GenerateUncached
@GenerateInline(false)
Expand All @@ -111,7 +113,14 @@ public static class AllPythonFramesSelector implements FrameSelector {

@Override
public boolean skip(RootNode rootNode) {
return PBytecodeDSLRootNode.cast(rootNode) == null;
PBytecodeDSLRootNode bytecodeDSLRootNode = PBytecodeDSLRootNode.cast(rootNode);
return bytecodeDSLRootNode == null || hasNoPythonFrame(bytecodeDSLRootNode);
}

@TruffleBoundary
private static boolean hasNoPythonFrame(PBytecodeDSLRootNode rootNode) {
Source source = rootNode.getSource();
return source != null && source.getOptions(PythonLanguage.get(rootNode)).get(PythonSourceOptions.NoPythonFrame);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,8 @@ private PythonSourceOptions() {
@Option(category = OptionCategory.USER, stability = OptionStability.STABLE, help = "Run this source with a fresh globals dictionary instead of the main module globals") //
public static final OptionKey<Boolean> NewGlobals = new OptionKey<>(false);

@Option(category = OptionCategory.INTERNAL, stability = OptionStability.STABLE, help = "Do not expose this source as a Python frame") //
public static final OptionKey<Boolean> NoPythonFrame = new OptionKey<>(false);

public static final OptionDescriptors DESCRIPTORS = new PythonSourceOptionsOptionDescriptors();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -61,7 +61,7 @@
*/
public class Py2BinLauncher {

public static void main(String[] args) throws IOException {
public static void main(String[] args) throws IOException {
Builder builder = GraalPyResources.contextBuilder()
.allowExperimentalOptions(true)
.allowAllAccess(true)
Expand All @@ -72,7 +72,7 @@ public static void main(String[] args) throws IOException {
}
try (var context = builder.build()) {
try {
var src = Source.newBuilder("python", "__graalpython__.run_path()", "<internal>").internal(true).build();
var src = Source.newBuilder("python", "__graalpython__.run_path()", "<internal>").internal(true).option("python.NoPythonFrame", "true").build();
context.eval(src);
} catch (PolyglotException e) {
if (e.isExit()) {
Expand All @@ -82,7 +82,7 @@ public static void main(String[] args) throws IOException {
}
}
}
}
}

private static String getProgramName() {
if (ImageInfo.inImageRuntimeCode()) {
Expand Down
44 changes: 10 additions & 34 deletions mx.graalpython/mx_graalpython_bisect.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@
import argparse
import json
import os
import re
import shlex
import sys
import types
from pathlib import Path

Expand Down Expand Up @@ -251,7 +249,14 @@ def __str__(self):
return "works" if self.value == 0 else "doesn't work"


def _bisect_benchmark(argv, bisect_id, email_to):
def filter_benchmark_results(docs, benchmark_name):
exact_docs = [x for x in docs if x['benchmark'] == benchmark_name]
if exact_docs:
return exact_docs
return [x for x in docs if x['benchmark'].endswith(f'.{benchmark_name}')]


def _bisect_benchmark(argv, bisect_id):
default_metric = 'time'
if 'BISECT_BENCHMARK_CONFIG' in os.environ:
import configparser
Expand Down Expand Up @@ -360,7 +365,7 @@ def benchmark_callback(repo_path: Path, commit, bench_command=args.benchmark_com
data = json.load(f)
docs = [x for x in data['queries'] if x['metric.name'] == args.benchmark_metric]
if args.benchmark_name:
docs = [x for x in docs if x['benchmark'] == args.benchmark_name]
docs = filter_benchmark_results(docs, args.benchmark_name)
if not docs:
raise RuntimeError(f"Couldn't find specified metric {args.benchmark_metric!r} in the results")
if len(docs) > 1:
Expand Down Expand Up @@ -416,41 +421,12 @@ def benchmark_callback(repo_path: Path, commit, bench_command=args.benchmark_com
print_line(40)
mx.run(shlex.split(cmd.strip()), nonZeroIsFatal=False)

send_email(
bisect_id,
email_to,
"Bisection job has finished successfully.\n{}\n".format(summary)
+ "Note I'm just a script and I don't validate statistical significance of the above result.\n"
+ "Please take a moment to also inspect the detailed results below.\n\n{}\n\n".format(visualization)
+ os.environ.get('BUILD_URL', 'Unknown URL')
)


def bisect_benchmark(argv):
initial_branch = GIT.git_command(DIR, ['rev-parse', '--abbrev-ref', 'HEAD']).strip()
initial_commit = GIT.git_command(DIR, ['log', '--format=%s', '-n', '1']).strip()
email_to = GIT.git_command(DIR, ['log', '--format=%cE', '-n', '1']).strip()
bisect_id = f'{initial_branch}: {initial_commit}'
try:
_bisect_benchmark(argv, bisect_id, email_to)
_bisect_benchmark(argv, bisect_id)
finally:
GIT.update_to_branch(DIR, initial_branch)


def send_email(bisect_id, email_to, content):
if 'BISECT_EMAIL_SMTP_SERVER' in os.environ:
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg['Subject'] = "Bisection result for {}".format(bisect_id)
msg['From'] = os.environ['BISECT_EMAIL_FROM']
validate_to = os.environ['BISECT_EMAIL_TO_PATTERN']
if not re.match(validate_to, email_to):
sys.exit("Email {} not allowed, aborting sending".format(email_to))
msg['To'] = email_to
msg.set_content(content)
print(msg)
smtp = smtplib.SMTP(os.environ['BISECT_EMAIL_SMTP_SERVER'])
smtp.send_message(msg)
smtp.quit()
41 changes: 1 addition & 40 deletions scripts/bisect_benchmark_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,45 +273,6 @@ def extract_commands(log: str, benchmark_name: str) -> tuple[str, str]:
return build_commands[-1], narrow_command(benchmark_commands[-1], benchmark_name)


def benchmark_match_score(candidate: str, selector: str) -> int:
if candidate == selector:
return 100
if candidate.rsplit(".", 1)[-1] == selector:
return 90
if candidate.rsplit(":", 1)[-1] == selector:
return 80
if candidate.endswith(".{}".format(selector)):
return 70
if candidate.endswith(":{}".format(selector)):
return 60
return 0


def resolve_results_benchmark_name(build_url: str, selector: str, metric: str) -> str | None:
if metric == "WORKS":
return None
data = json.loads(fetch_uploaded_log_text(build_url, "bench-results.json"))
candidates: list[tuple[int, int, str]] = []
for index, document in enumerate(data.get("queries", [])):
if document.get("metric.name") != metric:
continue
benchmark = document.get("benchmark")
if not isinstance(benchmark, str):
continue
score = benchmark_match_score(benchmark, selector)
if score > 0:
candidates.append((score, index, benchmark))
if not candidates:
return None
candidates.sort()
best_score = candidates[-1][0]
best_matches = [benchmark for score, _index, benchmark in candidates if score == best_score]
if len(set(best_matches)) != 1:
return None
best_match = best_matches[-1]
return best_match if best_match != selector else None


def build_config_text(
build_command: str,
benchmark_command: str,
Expand Down Expand Up @@ -556,7 +517,7 @@ def generate_config(
debug("Using reference build {} ({})".format(reference_build.build_number, reference_build.url))
build_log = run_command(["gdev-cli", "buildbot", "get-log", str(reference_build.build_number)], cwd=repo_dir)
build_command, benchmark_command = extract_commands(build_log, benchmark_name)
results_benchmark_name = resolve_results_benchmark_name(reference_build.url, benchmark_name, metric)
results_benchmark_name = None if metric == "WORKS" else benchmark_selector_for_command(benchmark_name)
enterprise = "enterprise" in build_command
return build_config_text(
build_command=build_command,
Expand Down
Loading