Skip to content

Commit eedf8f0

Browse files
committed
[GR-63688][GR-63689] Add sys.graalpy_version_info
PullRequest: graalpython/3742
2 parents 72c627a + e56c5ca commit eedf8f0

File tree

11 files changed

+114
-30
lines changed

11 files changed

+114
-30
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ This changelog summarizes major changes between GraalVM versions of the Python
44
language runtime. The main focus is on user-observable behavior of the engine.
55

66
## Version 25.0.0
7+
* `sys.implementation.version` now returns the GraalPy version instead of the Python version it implements. Also available as `sys.graalpy_version_info` for better discoverability by people already familiar with PyPy and its `sys.pypy_version_info`.
8+
* `GRAALPY_VERSION_NUM` C macro now inlcudes the release level and serial number at the end to conform to the `hexversion` format. This shouldn't break any existing comparisons.
79
* `dir(foreign_object)` now returns both foreign methods and Python methods (it used to return only foreign methods).
810
* Support `__name__`, `__doc__`, `__text_signature__` fields on foreign executables to serve as their proper counterparts on the Python side. This is useful to, for example, use Java functional interfaces in lieu of Python functions for things like LangChain's `@tool` annotation that want to inspect the underlying function.
911

graalpython/com.oracle.graal.python.cext/src/moduleobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ PyModule_GetFilenameObject(PyObject *m)
574574
Py_INCREF(fileobj);
575575
return fileobj;
576576
}
577+
#endif // GraalPy change
577578

578579
const char *
579580
PyModule_GetFilename(PyObject *m)
@@ -587,7 +588,6 @@ PyModule_GetFilename(PyObject *m)
587588
Py_DECREF(fileobj); /* module dict has still a reference */
588589
return utf8;
589590
}
590-
#endif // GraalPy change
591591

592592
PyModuleDef*
593593
PyModule_GetDef(PyObject* m)

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_misc.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -359,14 +359,12 @@ def test_graalpy_version():
359359
{"get_version_num", (PyCFunction)get_version_num, METH_NOARGS | METH_STATIC, ""}
360360
''',
361361
)
362-
expected_version = __graalpython__.get_graalvm_version().removesuffix('-dev')
363-
assert tester.get_version_str() == expected_version
364-
parts = [int(v) for v in expected_version.split('.')] + [0]
365-
expected_num = 0
366-
for i in range(3):
367-
expected_num <<= 8
368-
expected_num |= parts[i]
369-
assert tester.get_version_num() == expected_num
362+
version = sys.implementation.version
363+
assert tester.get_version_str() == f'{version.major}.{version.minor}.{version.micro}'
364+
assert tester.get_version_num() == sys.implementation.hexversion
365+
# This is an anti-backport trap. The commit that changed the hexversion format should not be backported because
366+
# existing projects might already contain tests for the next feature release in the old format (pybind11 does)
367+
assert version.major >= 25
370368

371369

372370
def test_unicode_docstring():

graalpython/com.oracle.graal.python.test/src/tests/cpyext/test_module.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -54,6 +54,22 @@ def _reference_add_object(args):
5454
args[0].__dict__[args[1]] = args[2]
5555
return 0
5656

57+
58+
def _reference_get_filename(args):
59+
if not isinstance(args[0], ModuleType):
60+
raise TypeError
61+
file = getattr(args[0], '__file__', None)
62+
if isinstance(file, str):
63+
return file
64+
raise SystemError
65+
66+
67+
module_with_file = ModuleType("module")
68+
module_with_file.__file__ = 'file.py'
69+
module_with_broken_file = ModuleType("module")
70+
module_with_broken_file.__file__ = 1
71+
72+
5773
class TestPyModule(CPyExtTestCase):
5874

5975
test_PyModule_Check = CPyExtFunction(
@@ -175,3 +191,31 @@ class TestPyModule(CPyExtTestCase):
175191
arguments=["PyObject* m", "const char* k", "PyObject* v"],
176192
cmpfunc=unhandled_error_compare
177193
)
194+
195+
test_PyModule_GetFilenameObject = CPyExtFunction(
196+
_reference_get_filename,
197+
lambda: (
198+
(module_with_file,),
199+
(module_with_broken_file,),
200+
(ModuleType("no-file"),),
201+
(1,),
202+
),
203+
resultspec="O",
204+
argspec='O',
205+
arguments=["PyObject* m"],
206+
cmpfunc=unhandled_error_compare
207+
)
208+
209+
test_PyModule_GetFilename = CPyExtFunction(
210+
_reference_get_filename,
211+
lambda: (
212+
(module_with_file,),
213+
(module_with_broken_file,),
214+
(ModuleType("no-file"),),
215+
(1,),
216+
),
217+
resultspec="s",
218+
argspec='O',
219+
arguments=["PyObject* m"],
220+
cmpfunc=unhandled_error_compare
221+
)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonOS.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -45,25 +45,31 @@
4545
import com.oracle.truffle.api.strings.TruffleString;
4646

4747
public enum PythonOS {
48-
PLATFORM_JAVA("java"),
49-
PLATFORM_CYGWIN("cygwin"),
50-
PLATFORM_LINUX("linux"),
51-
PLATFORM_DARWIN("darwin"),
52-
PLATFORM_WIN32("win32"),
53-
PLATFORM_SUNOS("sunos"),
54-
PLATFORM_FREEBSD("freebsd"),
55-
PLATFORM_ANY(null);
48+
PLATFORM_JAVA("java", "Java"),
49+
PLATFORM_CYGWIN("cygwin", "CYGWIN"),
50+
PLATFORM_LINUX("linux", "Linux"),
51+
PLATFORM_DARWIN("darwin", "Darwin"),
52+
PLATFORM_WIN32("win32", "Windows"),
53+
PLATFORM_SUNOS("sunos", "SunOS"),
54+
PLATFORM_FREEBSD("freebsd", "FreeBSD"),
55+
PLATFORM_ANY(null, null);
5656

5757
private final TruffleString name;
58+
private final TruffleString uname;
5859

59-
PythonOS(String name) {
60+
PythonOS(String name, String uname) {
6061
this.name = toTruffleStringUncached(name);
62+
this.uname = toTruffleStringUncached(uname);
6163
}
6264

6365
public TruffleString getName() {
6466
return name;
6567
}
6668

69+
public TruffleString getUname() {
70+
return uname;
71+
}
72+
6773
private static final PythonOS current;
6874

6975
static {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
package com.oracle.graal.python.builtins.modules;
4242

4343
import static com.oracle.graal.python.PythonLanguage.J_GRAALPYTHON_ID;
44+
import static com.oracle.graal.python.PythonLanguage.RELEASE_LEVEL;
45+
import static com.oracle.graal.python.PythonLanguage.RELEASE_SERIAL;
4446
import static com.oracle.graal.python.PythonLanguage.T_GRAALPYTHON_ID;
4547
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.AttributeError;
4648
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.DeprecationWarning;
@@ -506,16 +508,16 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
506508
return SysModuleBuiltinsFactory.getFactories();
507509
}
508510

509-
private static PSimpleNamespace makeImplementation(PythonLanguage language, PTuple versionInfo, TruffleString gmultiarch) {
511+
private static PSimpleNamespace makeImplementation(PythonLanguage language, PTuple graalpyVersionInfo, TruffleString gmultiarch) {
510512
final PSimpleNamespace ns = PFactory.createSimpleNamespace(language);
511513
ns.setAttribute(tsLiteral("name"), T_GRAALPYTHON_ID);
512514
/*- 'cache_tag' must match the format of mx.graalpython/mx_graalpython.py:graalpy_ext */
513515
ns.setAttribute(T_CACHE_TAG, toTruffleStringUncached(J_GRAALPYTHON_ID +
514516
PythonLanguage.GRAALVM_MAJOR + PythonLanguage.GRAALVM_MINOR + PythonLanguage.DEV_TAG +
515517
"-" + PythonLanguage.MAJOR + PythonLanguage.MINOR));
516-
ns.setAttribute(T_VERSION, versionInfo);
518+
ns.setAttribute(T_VERSION, graalpyVersionInfo);
517519
ns.setAttribute(T__MULTIARCH, gmultiarch);
518-
ns.setAttribute(tsLiteral("hexversion"), PythonLanguage.VERSION_HEX);
520+
ns.setAttribute(tsLiteral("hexversion"), PythonLanguage.GRAALVM_MAJOR << 24 | PythonLanguage.GRAALVM_MINOR << 16 | PythonLanguage.GRAALVM_MICRO << 8 | RELEASE_LEVEL << 4 | RELEASE_SERIAL);
519521
return ns;
520522
}
521523

@@ -588,7 +590,10 @@ public void initialize(Python3Core core) {
588590
addBuiltinConstant(T_STDOUT, PNone.NONE);
589591
addBuiltinConstant(T_STDERR, PNone.NONE);
590592

591-
addBuiltinConstant("implementation", makeImplementation(language, versionInfo, gmultiarch));
593+
PTuple graalpyVersion = PFactory.createStructSeq(language, VERSION_INFO_DESC, PythonLanguage.GRAALVM_MAJOR, PythonLanguage.GRAALVM_MINOR, PythonLanguage.GRAALVM_MICRO,
594+
PythonLanguage.RELEASE_LEVEL_STRING, PythonLanguage.RELEASE_SERIAL);
595+
addBuiltinConstant("graalpy_version_info", graalpyVersion);
596+
addBuiltinConstant("implementation", makeImplementation(language, graalpyVersion, gmultiarch));
592597
addBuiltinConstant("hexversion", PythonLanguage.VERSION_HEX);
593598

594599
if (os == PLATFORM_WIN32) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.PyObjectTransfer;
5454
import static com.oracle.graal.python.nodes.ErrorMessages.S_NEEDS_S_AS_FIRST_ARG;
5555
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___DOC__;
56+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__;
5657
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___NAME__;
5758
import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___PACKAGE__;
5859
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
@@ -65,6 +66,7 @@
6566
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiTernaryBuiltinNode;
6667
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode;
6768
import com.oracle.graal.python.builtins.modules.cext.PythonCextMethodBuiltins.CFunctionNewExMethodNode;
69+
import com.oracle.graal.python.builtins.objects.PNone;
6870
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CheckPrimitiveFunctionResultNode;
6971
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ExternalFunctionInvokeNode;
7072
import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.PExternalFunctionWrapper;
@@ -94,6 +96,7 @@
9496
import com.oracle.truffle.api.dsl.Bind;
9597
import com.oracle.truffle.api.dsl.Cached;
9698
import com.oracle.truffle.api.dsl.Cached.Shared;
99+
import com.oracle.truffle.api.dsl.Fallback;
97100
import com.oracle.truffle.api.dsl.ImportStatic;
98101
import com.oracle.truffle.api.dsl.Specialization;
99102
import com.oracle.truffle.api.interop.InteropLibrary;
@@ -297,4 +300,26 @@ static int doGeneric(PythonModule self, Object visitFun, Object arg,
297300
return 0;
298301
}
299302
}
303+
304+
@CApiBuiltin(ret = PyObjectTransfer, args = {PyObject}, call = Direct)
305+
abstract static class PyModule_GetFilenameObject extends CApiUnaryBuiltinNode {
306+
@Specialization
307+
static Object getFilename(PythonModule module,
308+
@Bind Node inliningTarget,
309+
@Cached ReadAttributeFromObjectNode read,
310+
@Cached PyUnicodeCheckNode check,
311+
@Cached PRaiseNode raiseNode) {
312+
Object file = read.execute(module, T___FILE__);
313+
if (file != PNone.NO_VALUE && check.execute(inliningTarget, file)) {
314+
return file;
315+
}
316+
throw raiseNode.raise(inliningTarget, SystemError, ErrorMessages.MODULE_FILENAME_MISSING);
317+
}
318+
319+
@Fallback
320+
static Object error(@SuppressWarnings("unused") Object module,
321+
@Bind Node inliningTarget) {
322+
throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC);
323+
}
324+
}
300325
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ public final class CApiFunction {
350350
@CApiBuiltin(name = "PyModule_ExecDef", ret = Int, args = {PyObject, PyModuleDef}, call = CImpl)
351351
@CApiBuiltin(name = "PyModule_GetDef", ret = PyModuleDef, args = {PyObject}, call = CImpl)
352352
@CApiBuiltin(name = "PyModule_GetDict", ret = PyObject, args = {PyObject}, call = CImpl)
353+
@CApiBuiltin(name = "PyModule_GetFilename", ret = ConstCharPtrAsTruffleString, args = {PyObject}, call = CImpl)
353354
@CApiBuiltin(name = "PyModule_GetName", ret = ConstCharPtrAsTruffleString, args = {PyObject}, call = CImpl)
354355
@CApiBuiltin(name = "PyModule_GetState", ret = Pointer, args = {PyObject}, call = CImpl)
355356
@CApiBuiltin(name = "PyNumber_Absolute", ret = PyObject, args = {PyObject}, call = CImpl)
@@ -827,8 +828,6 @@ public final class CApiFunction {
827828
@CApiBuiltin(name = "PyMem_SetupDebugHooks", ret = Void, args = {}, call = NotImplemented)
828829
@CApiBuiltin(name = "PyMember_GetOne", ret = PyObject, args = {ConstCharPtrAsTruffleString, PyMemberDef}, call = NotImplemented)
829830
@CApiBuiltin(name = "PyMember_SetOne", ret = Int, args = {CHAR_PTR, PyMemberDef, PyObject}, call = NotImplemented)
830-
@CApiBuiltin(name = "PyModule_GetFilename", ret = ConstCharPtrAsTruffleString, args = {PyObject}, call = NotImplemented)
831-
@CApiBuiltin(name = "PyModule_GetFilenameObject", ret = PyObject, args = {PyObject}, call = NotImplemented)
832831
@CApiBuiltin(name = "PyODict_DelItem", ret = Int, args = {PyObject, PyObject}, call = NotImplemented)
833832
@CApiBuiltin(name = "PyODict_New", ret = PyObject, args = {}, call = NotImplemented)
834833
@CApiBuiltin(name = "PyODict_SetItem", ret = Int, args = {PyObject, PyObject, PyObject}, call = NotImplemented)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/ErrorMessages.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ public abstract class ErrorMessages {
504504
public static final TruffleString MISSING_S = tsLiteral("Missing %s");
505505
public static final TruffleString S_MISSING_REQUIRED_ARG_POS_D = tsLiteral("%s missing required argument (pos %d)");
506506
public static final TruffleString MMAP_INDEX_OUT_OF_RANGE = tsLiteral("mmap index out of range");
507+
public static final TruffleString MODULE_FILENAME_MISSING = tsLiteral("module filename missing");
507508
public static final TruffleString MODULE_HAS_NO_ATTR_S = tsLiteral("module has no attribute '%s'");
508509
public static final TruffleString MODULE_PARTIALLY_INITIALIZED_S_HAS_NO_ATTR_S = tsLiteral("partially initialized module '%s' has no attribute '%s' (most likely due to a circular import)");
509510
public static final TruffleString MODULE_S_HAS_NO_ATTR_S = tsLiteral("module '%s' has no attribute '%s'");

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@
210210
import java.util.concurrent.TimeUnit;
211211
import java.util.logging.Level;
212212

213-
import com.oracle.truffle.api.TruffleOptions;
214213
import org.graalvm.nativeimage.ImageInfo;
215214
import org.graalvm.nativeimage.ProcessProperties;
216215
import org.graalvm.polyglot.io.ProcessHandler.Redirect;
@@ -252,6 +251,7 @@
252251
import com.oracle.truffle.api.TruffleLanguage;
253252
import com.oracle.truffle.api.TruffleLanguage.Env;
254253
import com.oracle.truffle.api.TruffleLogger;
254+
import com.oracle.truffle.api.TruffleOptions;
255255
import com.oracle.truffle.api.TruffleSafepoint;
256256
import com.oracle.truffle.api.dsl.Bind;
257257
import com.oracle.truffle.api.dsl.Cached;
@@ -1290,7 +1290,7 @@ private static int posixPermissionsToMode(int inputMode, final Set<PosixFilePerm
12901290
@SuppressWarnings("static-method")
12911291
public Object[] uname(
12921292
@Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) {
1293-
return new Object[]{getPythonOS().getName(), fromJavaStringNode.execute(getHostName(withoutIOSocket), TS_ENCODING),
1293+
return new Object[]{getPythonOS().getUname(), fromJavaStringNode.execute(getHostName(withoutIOSocket), TS_ENCODING),
12941294
fromJavaStringNode.execute(getOsVersion(), TS_ENCODING), T_EMPTY_STRING, PythonUtils.getPythonArch()};
12951295
}
12961296

mx.graalpython/mx_graalpython.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,8 @@ def graal_version_short(variant=None, **kwargs):
18611861
for i in range(3):
18621862
num <<= 8
18631863
num |= int(parts[i]) if i < len(parts) else 0
1864+
num <<= 8
1865+
num |= release_level('int') << 4
18641866
return hex(num)
18651867
else:
18661868
return '.'.join(GRAAL_VERSION.split('.')[:3])
@@ -1871,14 +1873,16 @@ def release_level(variant=None):
18711873
level = 'alpha'
18721874
if SUITE.suiteDict['release']:
18731875
level = 'final'
1874-
if variant == 'binary':
1876+
if variant in ('binary', 'int'):
18751877
level_num = {
18761878
'alpha': 0xA,
18771879
'beta': 0xB,
18781880
'candidate': 0xC,
18791881
'final': 0xF,
18801882
}[level]
1881-
return chr(level_num + ord(VERSION_BASE))
1883+
if variant == 'binary':
1884+
return chr(level_num + ord(VERSION_BASE))
1885+
return level_num
18821886
return level
18831887

18841888

0 commit comments

Comments
 (0)