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
3 changes: 1 addition & 2 deletions graalpython/com.oracle.graal.python.cext/src/capi.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,11 @@ static void initialize_builtin_types_and_structs() {
}

static int mmap_getbuffer(PyObject *self, Py_buffer *view, int flags) {
// TODO(fa) readonly flag
char* data = GraalPyPrivate_GetMMapData(self);
if (!data) {
return -1;
}
return PyBuffer_FillInfo(view, (PyObject*)self, data, PyObject_Size((PyObject *)self), 0, flags);
return PyBuffer_FillInfo(view, (PyObject*)self, data, PyObject_Size((PyObject *)self), GraalPyPrivate_MMap_IsReadonly(self), flags);
}

GraalPy_CAPI_HELPER_SYMBOL void GraalPyPrivate_MMap_InitBufferProtocol(PyObject* mmap_type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2018, 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 @@ -91,3 +91,14 @@ def test_name_qualname(self):
assert HeapTypeWithName.__name__ == 'HeapTypeWithNameRenamed'
HeapTypeWithName.__qualname__ = 'foo.HeapTypeWithNameRenamed'
assert HeapTypeWithName.__qualname__ == 'foo.HeapTypeWithNameRenamed'

DottedModuleType = CPyExtHeapType(
'DottedModuleType',
ready_code='spec.name = "pkg.mod.DottedModuleType";',
slots=[
'{Py_tp_doc, (void *)"DottedModuleType($self, /)\\n--\\n\\nReturn dotted module type metadata."}',
],
)
assert DottedModuleType.__name__ == 'DottedModuleType'
assert DottedModuleType.__module__ == 'pkg.mod'
assert DottedModuleType.__doc__ == 'Return dotted module type metadata.'
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,34 @@

__global_builtins_dict = builtins.__dict__

FSPathUnicodeSubclass = CPyExtType(
"FSPathUnicodeSubclass",
'',
struct_base='PyUnicodeObject base;',
tp_base='&PyUnicode_Type',
tp_new='0',
tp_alloc='0',
tp_free='0',
)

FSPathBytesSubclass = CPyExtType(
"FSPathBytesSubclass",
'',
struct_base='PyBytesObject base;',
tp_itemsize='sizeof(char)',
tp_base='&PyBytes_Type',
tp_new='0',
tp_alloc='0',
tp_free='0',
)

class FSPathWrapper:
def __init__(self, value):
self.value = value

def __fspath__(self):
return self.value


def _reference_importmodule(args):
return __import__(args[0], fromlist=["*"])
Expand Down Expand Up @@ -366,6 +394,8 @@ def test_sre_bytes_empty_user(self):
(b"bytespath",),
("stringpath",),
(pathlib.Path("pathpath"),),
(FSPathWrapper(FSPathUnicodeSubclass("native-stringpath")),),
(FSPathWrapper(FSPathBytesSubclass(b"native-bytespath")),),
(123,),
(object(),),
),
Expand All @@ -381,6 +411,37 @@ def test_sre_bytes_empty_user(self):
cmpfunc=unhandled_error_compare
)

test_PyOS_FSPath_native_subclass = CPyExtFunction(
lambda args: 1,
lambda: (
(FSPathUnicodeSubclass("stringpath"),),
(FSPathBytesSubclass(b"bytespath"),),
),
callfunction="call_PyOS_FSPath_native_subclass",
code="""
static int call_PyOS_FSPath_native_subclass(PyObject* value) {
PyObject* result = PyOS_FSPath(value);
if (result == NULL) {
return -1;
}
int same = result == value;
Py_DECREF(result);
return same;
}
""",
resultspec="i",
argspec="O",
arguments=["PyObject* value"],
)

def test_os_fspath_native_subclass(self):
for value in (FSPathUnicodeSubclass("stringpath"), FSPathBytesSubclass(b"bytespath")):
assert os.fspath(value) is value

def test_os_fspath_native_subclass_return(self):
for value in (FSPathUnicodeSubclass("stringpath"), FSPathBytesSubclass(b"bytespath")):
assert os.fspath(FSPathWrapper(value)) is value


@unittest.skipUnless(sys.implementation.name == 'graalpy', "GraalPy-only")
def test_graalpy_version():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,21 @@ def g2(seqn):
self.assertEqual(list(tee(g([1, 2]))[0]), [1, 2])
self.assertEqual(list(zip_longest(g2([2,3]))), [((2, 2),), ((3, 3),)])

def test_product_repeat_index(self):
class Index:
def __init__(self, value):
self.value = value

def __index__(self):
return self.value

self.assertEqual(list(product([1, 2], repeat=Index(2))), [(1, 1), (1, 2), (2, 1), (2, 2)])
self.assertEqual(list(product([1, 2], repeat=Index(0))), [()])
self.assertRaises(ValueError, product, [1, 2], repeat=Index(-1))
self.assertRaises(ValueError, product, [1, 2], repeat=-1)
self.assertRaises(TypeError, product, [1, 2], repeat=None)
self.assertRaises(TypeError, product, [1, 2], repeat=object())

def test_islice_negative_stop(self):
self.assertRaises(ValueError, islice, count(), -1)
self.assertRaises(ValueError, islice, count(), 0, -1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1659,4 +1659,10 @@ static long GraalPyPrivate_GetMMapData(long objectPtr) {
throw PConstructAndRaiseNode.getUncached().raiseOSErrorUnsupported(null, e);
}
}

@CApiBuiltin(ret = Int, args = {PyObjectRawPointer}, call = Ignored)
static int GraalPyPrivate_MMap_IsReadonly(long objectPtr) {
PMMap object = (PMMap) NativeToPythonInternalNode.executeUncached(objectPtr, false);
return object.isWriteable() ? 0 : 1;
}
}
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 @@ -251,9 +251,9 @@ String getMetaSimpleName(

@TruffleBoundary
private static String getSimpleName(String fqname) {
int firstDot = fqname.indexOf('.');
if (firstDot != -1) {
return fqname.substring(firstDot + 1);
int lastDot = fqname.lastIndexOf('.');
if (lastDot != -1) {
return fqname.substring(lastDot + 1);
}
return fqname;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 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,9 +41,11 @@
package com.oracle.graal.python.builtins.objects.itertools;

import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError;
import static com.oracle.graal.python.nodes.ErrorMessages.ARG_CANNOT_BE_NEGATIVE;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___REDUCE__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.J___SETSTATE__;
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___SETSTATE__;

import java.util.List;

Expand All @@ -59,21 +61,25 @@
import com.oracle.graal.python.builtins.modules.ItertoolsModuleBuiltins.DeprecatedSetStateBuiltin;
import com.oracle.graal.python.builtins.objects.PNone;
import com.oracle.graal.python.builtins.objects.iterator.IteratorNodes;
import com.oracle.graal.python.builtins.objects.list.PList;
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
import com.oracle.graal.python.builtins.objects.type.TpSlots;
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotIterNext.TpIterNextBuiltin;
import com.oracle.graal.python.lib.PyLongAsIntNode;
import com.oracle.graal.python.lib.PyNumberAsSizeNode;
import com.oracle.graal.python.lib.PyTupleCheckNode;
import com.oracle.graal.python.lib.PyTupleGetItem;
import com.oracle.graal.python.lib.PyTupleSizeNode;
import com.oracle.graal.python.nodes.ErrorMessages;
import com.oracle.graal.python.nodes.PRaiseNode;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
import com.oracle.graal.python.nodes.object.GetClassNode;
import com.oracle.graal.python.nodes.util.CannotCastException;
import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode;
import com.oracle.graal.python.runtime.object.PFactory;
import com.oracle.graal.python.util.PythonUtils;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
Expand Down Expand Up @@ -102,7 +108,7 @@ protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFa
@GenerateNodeFactory
public abstract static class ProductNode extends PythonBuiltinNode {

@Specialization(guards = "isTypeNode.execute(inliningTarget, cls)")
@Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "isNoValue(repeat)"})
static Object constructNoneRepeat(VirtualFrame frame, Object cls, Object[] iterables, @SuppressWarnings("unused") PNone repeat,
@SuppressWarnings("unused") @Bind Node inliningTarget,
@Cached.Shared @Cached IteratorNodes.ToArrayNode toArrayNode,
Expand All @@ -113,57 +119,47 @@ static Object constructNoneRepeat(VirtualFrame frame, Object cls, Object[] itera
return self;
}

@Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat == 1"}, limit = "1")
static Object constructOneRepeat(VirtualFrame frame, Object cls, Object[] iterables, @SuppressWarnings("unused") int repeat,
@SuppressWarnings("unused") @Bind Node inliningTarget,
@Cached.Shared @Cached IteratorNodes.ToArrayNode toArrayNode,
@SuppressWarnings("unused") @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode,
@Cached.Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
PProduct self = PFactory.createProduct(cls, getInstanceShape.execute(cls));
constructOneRepeat(frame, self, iterables, toArrayNode);
return self;
}

@Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat > 1"}, limit = "1")
static Object construct(VirtualFrame frame, Object cls, Object[] iterables, int repeat,
@Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "!isNoValue(repeat)"}, limit = "1")
static Object constructRepeat(VirtualFrame frame, Object cls, Object[] iterables, Object repeat,
@Bind Node inliningTarget,
@Exclusive @Cached PyNumberAsSizeNode asSizeNode,
@Cached.Shared @Cached IteratorNodes.ToArrayNode toArrayNode,
@Cached InlinedBranchProfile errorProfile,
@Cached InlinedBranchProfile zeroProfile,
@Cached InlinedBranchProfile oneProfile,
@Cached InlinedBranchProfile genericProfile,
@Cached InlinedLoopConditionProfile loopProfile,
@SuppressWarnings("unused") @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode,
@Cached.Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
Object[][] lists = unpackIterables(frame, iterables, toArrayNode);
Object[][] gears = new Object[lists.length * repeat][];
loopProfile.profileCounted(inliningTarget, repeat);
LoopNode.reportLoopCount(inliningTarget, repeat);
for (int i = 0; loopProfile.inject(inliningTarget, i < repeat); i++) {
PythonUtils.arraycopy(lists, 0, gears, i * lists.length, lists.length);
int repeatInt = asSizeNode.executeExact(frame, inliningTarget, repeat);
if (repeatInt < 0) {
errorProfile.enter(inliningTarget);
throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ARG_CANNOT_BE_NEGATIVE, "repeat");
}
PProduct self = PFactory.createProduct(cls, getInstanceShape.execute(cls));
construct(self, gears);
return self;
}

@Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat == 0"}, limit = "1")
static Object constructNoRepeat(Object cls, @SuppressWarnings("unused") Object[] iterables, @SuppressWarnings("unused") int repeat,
@SuppressWarnings("unused") @Bind Node inliningTarget,
@SuppressWarnings("unused") @Exclusive @Cached TypeNodes.IsTypeNode isTypeNode,
@Cached.Shared @Cached TypeNodes.GetInstanceShape getInstanceShape) {
PProduct self = PFactory.createProduct(cls, getInstanceShape.execute(cls));
self.setGears(new Object[0][]);
self.setIndices(new int[0]);
self.setLst(null);
self.setStopped(false);
if (repeatInt == 0) {
zeroProfile.enter(inliningTarget);
self.setGears(new Object[0][]);
self.setIndices(new int[0]);
self.setLst(null);
self.setStopped(false);
} else if (repeatInt == 1) {
oneProfile.enter(inliningTarget);
constructOneRepeat(frame, self, iterables, toArrayNode);
} else {
genericProfile.enter(inliningTarget);
Object[][] lists = unpackIterables(frame, iterables, toArrayNode);
Object[][] gears = new Object[lists.length * repeatInt][];
loopProfile.profileCounted(inliningTarget, repeatInt);
LoopNode.reportLoopCount(inliningTarget, repeatInt);
for (int i = 0; loopProfile.inject(inliningTarget, i < repeatInt); i++) {
PythonUtils.arraycopy(lists, 0, gears, i * lists.length, lists.length);
}
construct(self, gears);
}
return self;
}

@SuppressWarnings("unused")
@Specialization(guards = {"isTypeNode.execute(inliningTarget, cls)", "repeat < 0"}, limit = "1")
static Object constructNeg(Object cls, Object[] iterables, int repeat,
@Bind Node inliningTarget,
@Exclusive @Cached TypeNodes.IsTypeNode isTypeNode) {
throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ARG_CANNOT_BE_NEGATIVE, "repeat");
}

private static void constructOneRepeat(VirtualFrame frame, PProduct self, Object[] iterables, IteratorNodes.ToArrayNode toArrayNode) {
Object[][] gears = unpackIterables(frame, iterables, toArrayNode);
construct(self, gears);
Expand Down Expand Up @@ -323,37 +319,46 @@ static Object reduce(PProduct self) {
}

private static PTuple createGearTuple(PProduct self, PythonLanguage language) {
PList[] lists = new PList[self.getGears().length];
for (int i = 0; i < lists.length; i++) {
lists[i] = PFactory.createList(language, self.getGears()[i]);
PTuple[] tuples = new PTuple[self.getGears().length];
for (int i = 0; i < tuples.length; i++) {
tuples[i] = PFactory.createTuple(language, self.getGears()[i]);
}
return PFactory.createTuple(language, lists);
return PFactory.createTuple(language, tuples);
}
}

@Builtin(name = J___SETSTATE__, minNumOfPositionalArgs = 2)
@GenerateNodeFactory
public abstract static class SetStateNode extends DeprecatedSetStateBuiltin {
@Specialization
static Object setState(PProduct self, Object state) {
@TruffleBoundary
static Object setState(PProduct self, Object state,
@Bind Node inliningTarget) {
Object[][] gears = self.getGears();
if (!PyTupleCheckNode.executeUncached(state) || PyTupleSizeNode.executeUncached(state) != gears.length) {
throw PRaiseNode.raiseStatic(inliningTarget, ValueError, ErrorMessages.INVALID_ARGS, T___SETSTATE__);
}
Object[] lst = new Object[gears.length];
int[] indices = self.getIndices();
for (int i = 0; i < gears.length; i++) {
Object o = PyTupleGetItem.executeUncached(state, i);
int index = PyLongAsIntNode.executeUncached(o);
int gearSize = gears[i].length;
if (indices == null || gearSize == 0) {
self.setStopped(true);
return PNone.NONE;
}
if (index < 0) {
index = 0;
} else if (index > gearSize - 1) {
index = gearSize - 1;
try {
for (int i = 0; i < gears.length; i++) {
Object o = PyTupleGetItem.executeUncached(state, i);
int index = CastToJavaIntExactNode.executeUncached(o);
int gearSize = gears[i].length;
if (indices == null || gearSize == 0) {
self.setStopped(true);
return PNone.NONE;
}
if (index < 0) {
index = 0;
} else if (index > gearSize - 1) {
index = gearSize - 1;
}
indices[i] = index;
lst[i] = gears[i][index];
}
indices[i] = index;
lst[i] = gears[i][index];
} catch (CannotCastException e) {
throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.INTEGER_REQUIRED);
}
self.setLst(lst);
return PNone.NONE;
Expand Down
Loading
Loading