Skip to content
Merged
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: 1 addition & 1 deletion .github/workflows/downstream-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
echo "${HOME}/.cargo/bin" >> $GITHUB_PATH

- name: Install uv
if: ${{ matrix.name == 'pydantic-core' }}
if: ${{ matrix.name == 'pydantic-core' || matrix.name == 'jiter' }}
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
Expand Down
17 changes: 16 additions & 1 deletion graalpython/com.oracle.graal.python.test/src/tests/test_ast.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2022, 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 @@ -193,5 +193,20 @@ def test_parse_empty_module(self):
assert eval(code) is None
assert isinstance(code, types.CodeType)

def test_compile_match_singleton_none(self):
# This occurs in pytest-generated ASTs
m = ast.Module(
body=[
ast.Match(
subject=ast.Name("v", ast.Load()),
cases=[ast.match_case(pattern=ast.MatchSingleton(None), body=[ast.Pass()])],
)
],
type_ignores=[],
)
m = ast.fix_missing_locations(m)
code = compile(m, "<test>", "exec")
assert isinstance(code, types.CodeType)

if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def test_lookup(self):
with self.assertRaisesRegex(KeyError, "name too long"):
unicodedata.lookup("a" * 257)

def test_lookup_unknown_name_via_fallback(self):
with self.assertRaisesRegex(KeyError, "undefined character name 'GIBBET NICH'"):
unicodedata.lookup("GIBBET NICH")

def test_lookup_named_sequence(self):
if unicodedata.ucd_3_2_0.bidirectional == unicodedata.bidirectional:
raise unittest.SkipTest("Only supported with CPython's unicodedata.ucd_3_2_0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext;
import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData;
import com.oracle.graal.python.runtime.PythonOptions;
import com.oracle.graal.python.runtime.exception.PException;
import com.oracle.graal.python.nodes.statement.AbstractImportNode;
Expand All @@ -93,6 +95,7 @@
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.api.strings.TruffleString.FromJavaStringNode;
Expand Down Expand Up @@ -249,26 +252,23 @@ abstract static class LookupNode extends PythonUnaryClinicBuiltinNode {
private static final int NAME_MAX_LENGTH = 256;

@Specialization
@TruffleBoundary
static Object lookup(TruffleString name,
static Object lookup(VirtualFrame frame, TruffleString name,
@Bind PythonLanguage lang,
@Bind Node inliningTarget) {
@Bind Node inliningTarget,
@Cached("createFor($node)") BoundaryCallData boundaryCallData) {
String nameString = ToJavaStringNode.getUncached().execute(name);
if (nameString.length() > NAME_MAX_LENGTH) {
throw PRaiseNode.raiseStatic(inliningTarget, KeyError, ErrorMessages.NAME_TOO_LONG);
}

String character = getCharacterByUnicodeName(nameString);
if (character == null) {
character = getCharacterByUnicodeNameAlias(nameString);
}
if (character != null) {
return FromJavaStringNode.getUncached().execute(character, TS_ENCODING);
}

Object namedSequence = lookupNamedSequenceFromFallback(lang, name);
if (namedSequence != null) {
return namedSequence;
Object saved = BoundaryCallContext.enter(frame, boundaryCallData);
try {
Object result = lookupBoundary(lang, nameString, name);
if (result != null) {
return result;
}
} finally {
BoundaryCallContext.exit(frame, boundaryCallData, saved);
}
throw PRaiseNode.raiseStatic(inliningTarget, KeyError, ErrorMessages.UNDEFINED_CHARACTER_NAME, name);
}
Expand Down Expand Up @@ -301,6 +301,17 @@ private static String getCharacterByUnicodeNameAlias(String unicodeName) {
}

@TruffleBoundary
private static Object lookupBoundary(PythonLanguage lang, String nameString, TruffleString name) {
String character = getCharacterByUnicodeName(nameString);
if (character == null) {
character = getCharacterByUnicodeNameAlias(nameString);
}
if (character != null) {
return FromJavaStringNode.getUncached().execute(character, TS_ENCODING);
}
return lookupNamedSequenceFromFallback(lang, name);
}

private static Object lookupNamedSequenceFromFallback(PythonLanguage lang, TruffleString name) {
if (lang.getEngineOption(PythonOptions.UnicodeCharacterDatabaseNativeFallback)) {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 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 @@ -41,6 +41,7 @@
package com.oracle.graal.python.builtins.modules.ast;

import static com.oracle.graal.python.builtins.modules.ast.AstState.T_C_CONSTANT;
import static com.oracle.graal.python.builtins.modules.ast.AstState.T_C_MATCHSINGLETON;
import static com.oracle.graal.python.builtins.modules.ast.AstState.T_F_VALUE;
import static com.oracle.graal.python.nodes.ErrorMessages.AST_IDENTIFIER_MUST_BE_OF_TYPE_STR;
import static com.oracle.graal.python.nodes.ErrorMessages.EXPECTED_SOME_SORT_OF_S_BUT_GOT_S;
Expand Down Expand Up @@ -124,7 +125,7 @@ <T> T lookupAndConvert(Object obj, TruffleString attrName, TruffleString nodeNam
// since our SST nodes are in the pegparser project which does not have access to Python
// exceptions. So we handle PNone.NONE here, but there is one exception - None is a
// valid value for the required field ExprTy.Constant.value.
if (!(nodeName == T_C_CONSTANT && attrName == T_F_VALUE)) {
if (!((nodeName == T_C_CONSTANT || nodeName == T_C_MATCHSINGLETON) && attrName == T_F_VALUE)) {
throw raiseValueError(FIELD_S_IS_REQUIRED_FOR_S, attrName, nodeName);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2683,7 +2683,7 @@ public void initializeMultiThreading() {
handler.activateGIL();
}

public synchronized void attachThread(Thread thread, ContextThreadLocal<PythonThreadState> threadState) {
public void attachThread(Thread thread, ContextThreadLocal<PythonThreadState> threadState) {
CompilerAsserts.neverPartOfCompilation();
PythonThreadState pythonThreadState = threadState.get(thread);
threadStateMapping.put(thread, pythonThreadState);
Expand Down Expand Up @@ -2723,7 +2723,7 @@ public void initializeNativeThreadState(PythonThreadState pythonThreadState) {
}
}

public synchronized void disposeThread(Thread thread, boolean canRunGuestCode) {
public void disposeThread(Thread thread, boolean canRunGuestCode) {
CompilerAsserts.neverPartOfCompilation();
// check if there is a live sentinel lock
PythonThreadState ts = threadStateMapping.get(thread);
Expand Down
51 changes: 51 additions & 0 deletions graalpython/lib-graalpython/patches/librt-0.9.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
diff --git a/mypyc_util.h b/mypyc_util.h
index 115c309..d0c825f 100644
--- a/mypyc_util.h
+++ b/mypyc_util.h
@@ -82,7 +82,7 @@

static inline void CPy_INCREF_NO_IMM(PyObject *op)
{
- Py_REFCNT(op)++;
+ Py_INCREF(op);
}

static inline void CPy_DECREF_NO_IMM(PyObject *op)
diff --git a/pythonsupport.h b/pythonsupport.h
index 6f38a9b..59d5e67 100644
--- a/pythonsupport.h
+++ b/pythonsupport.h
@@ -117,6 +117,7 @@ CPyLong_AsSsize_tAndOverflow(PyObject *vv, int *overflow)
#endif

// Adapted from listobject.c in Python 3.7.0
+#if 0 // GraalPy change
static int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
@@ -162,6 +163,7 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
self->allocated = new_allocated;
return 0;
}
+#endif

// Changed to use PyList_SetSlice instead of the internal list_ass_slice
static PyObject *
@@ -182,6 +184,8 @@ list_pop_impl(PyListObject *self, Py_ssize_t index)
return NULL;
}
v = PySequence_Fast_ITEMS((PyObject*)self)[index];
+ Py_INCREF(v);
+#if 0 // GraalPy change
if (index == Py_SIZE(self) - 1) {
status = list_resize(self, Py_SIZE(self) - 1);
if (status >= 0)
@@ -189,7 +193,7 @@ list_pop_impl(PyListObject *self, Py_ssize_t index)
else
return NULL;
}
- Py_INCREF(v);
+#endif
status = PyList_SetSlice((PyObject *)self, index, index+1, (PyObject *)NULL);
if (status < 0) {
Py_DECREF(v);
5 changes: 5 additions & 0 deletions graalpython/lib-graalpython/patches/metadata.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,11 @@ version = '== 0.7.8'
patch = 'librt-0.7.8.patch'
license = 'MIT'

[[librt.rules]]
version = '== 0.9.0'
patch = 'librt-0.9.0.patch'
license = 'MIT'

[[opencv-python.rules]]
version = '== 4.12.*'
patch = 'opencv-python-4.12.patch'
Expand Down
61 changes: 42 additions & 19 deletions mx.graalpython/downstream_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,37 +156,60 @@ def downstream_test_pyo3(graalpy, testdir):
src = testdir / 'pyo3'
venv = src / 'venv'
run([graalpy, '-m', 'venv', str(venv)])
run_in_venv(venv, ['pip', 'install', 'nox'])
run_in_venv(venv, ['nox', '-s', 'test-py'], cwd=src)
run_in_venv(venv, ['python', '-m', 'pip', 'install', '--upgrade', 'pip', 'nox[uv]'])
env = os.environ.copy()
env['NOX_DEFAULT_VENV_BACKEND'] = 'uv'
run_in_venv(venv, ['nox', '-s', 'test-py'], cwd=src, env=env)


@downstream_test('pydantic-core')
def downstream_test_pydantic_core(graalpy, testdir):
run(['git', 'clone', 'https://github.com/pydantic/pydantic.git', '-b', 'main', '--depth', '1'], cwd=testdir)
src = testdir / 'pydantic' / 'pydantic-core'
run(['uv', 'sync', '--python', graalpy, '--group', 'testing-extra', '--no-install-project'], cwd=src)
run(['uv', 'build', '--python', graalpy, '--wheel', '.'], cwd=src)
[wheel] = (src.parent / 'dist').glob('pydantic_core-*graalpy*.whl')
# uv doesn't accept wheels with devtags
if match := re.search('dev[0-9a-f]{6,}', wheel.name):
wheel = wheel.rename(wheel.parent / wheel.name.replace(match.group(), ''))
run(['uv', 'pip', 'install', wheel], cwd=src)
repo = testdir / 'pydantic'
env = os.environ.copy()
env['HYPOTHESIS_PROFILE'] = 'slow'
run(['uv', 'run', '--no-sync', 'pytest', '-v', '--tb=short'], cwd=src, env=env)
env['UV_PYTHON_DOWNLOADS'] = 'never'
env['UV_PYTHON'] = str(graalpy)
# Needed for rpds-py dependency
env['UNSAFE_PYO3_SKIP_VERSION_CHECK'] = '1'
# Commands taken from upstream. The upstream has some TODOs so we'll likely need to sync this again soon
run(
['uv', 'sync', '--directory', 'pydantic-core', '--group', 'testing-extra', '--no-install-package', 'pydantic-core'],
cwd=repo,
env=env,
)
run(
[
'uv', 'sync', '--group', 'testing-extra', '--no-install-package', 'pydantic-core',
'--no-install-package', 'pytest-memray', '--no-install-package', 'memray',
'--no-install-package', 'pytest-codspeed', '--no-install-package', 'cffi', '--inexact',
# GraalPy change: greenlet crashes on import
'--no-install-package', 'greenlet',
],
cwd=repo,
env=env,
)
del env['UV_PYTHON']
run(
['uv', 'pip', 'install', './pydantic-core', '--no-deps', '--force-reinstall'],
cwd=repo,
env=env,
)
run(
['uv', 'run', '--no-sync', 'pytest', 'tests/pydantic_core', '--ignore=tests/pydantic_core/test_docstrings.py'],
cwd=repo,
env=env,
)


@downstream_test('jiter')
def downstream_test_jiter(graalpy, testdir):
run(['git', 'clone', 'https://github.com/pydantic/jiter.git', '-b', 'main', '--depth', '1'], cwd=testdir)
src = testdir / 'jiter'
venv = src / 'venv'
run([graalpy, '-m', 'venv', str(venv)])
run_in_venv(venv, ['pip', 'install', '-r', 'crates/jiter-python/tests/requirements.txt'], cwd=src)
run_in_venv(venv, ['pip', 'install', '-e', 'crates/jiter-python',
'--config-settings=build-args=--profile dev'], cwd=src)
run_in_venv(venv, ['pytest', '-v', '--tb=short', 'crates/jiter-python/tests'], cwd=src)
run_in_venv(venv, ['python', 'crates/jiter-python/bench.py', 'jiter', 'jiter-cache', '--fast'], cwd=src)
env = os.environ.copy()
env['UV_PYTHON_DOWNLOADS'] = 'never'
run(['uv', 'sync', '--python', graalpy, '--group', 'dev', '--all-packages'], cwd=src, env=env)
run(['uv', 'run', '--python', graalpy, 'pytest', '-v', '--tb=short', 'crates/jiter-python/tests'], cwd=src, env=env)
run(['uv', 'run', '--python', graalpy, 'crates/jiter-python/bench.py', 'jiter', 'jiter-cache', '--fast'], cwd=src, env=env)


@downstream_test('cython')
Expand Down
2 changes: 1 addition & 1 deletion mx.graalpython/mx_graalpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -1947,7 +1947,7 @@ def graalpy_ext(*_):


def dev_tag(_=None):
if not get_boolean_env('GRAALPYTHONDEVMODE', True) or 'dev' not in SUITE.release_version():
if not get_boolean_env('GRAALPYTHONDEVMODE', False) or 'dev' not in SUITE.release_version():
mx.logv("GraalPy dev_tag: <0 because not in dev mode>")
return ''

Expand Down
Loading