Skip to content

Commit b20f203

Browse files
committed
Add C API type 'PyCapsule'.
1 parent e122c8a commit b20f203

File tree

5 files changed

+139
-2
lines changed

5 files changed

+139
-2
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ static void initialize_capi() {
8383
initialize_type_structure(&PySlice_Type, "slice");
8484
initialize_type_structure(&PyByteArray_Type, "bytearray");
8585
initialize_type_structure(&_PyNotImplemented_Type, "NotImplementedType");
86+
initialize_type_structure(&PyCapsule_Type, "PyCapsule");
8687

8788
// initialize global variables like '_Py_NoneStruct', etc.
8889
initialize_globals();

graalpython/com.oracle.graal.python.cext/src/capi.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ typedef struct {
5151
PyObject *md_name; /* for logging purposes after md_dict is cleared */
5252
} PyModuleObject;
5353

54+
// taken from CPython "Objects/capsule.c"
55+
typedef struct {
56+
PyObject_HEAD
57+
void *pointer;
58+
const char *name;
59+
void *context;
60+
PyCapsule_Destructor destructor;
61+
} PyCapsule;
5462

5563
/* Declare Python structs/types for explicit polyglot typecasts. */
5664
/* NOTE: Also add an appropriate case in 'PyTruffle_Explicit_Cast' ! */
@@ -64,6 +72,7 @@ POLYGLOT_DECLARE_TYPE(PyDictObject);
6472
POLYGLOT_DECLARE_TYPE(PyUnicodeObject);
6573
POLYGLOT_DECLARE_TYPE(PyBytesObject);
6674
POLYGLOT_DECLARE_STRUCT(_longobject);
75+
POLYGLOT_DECLARE_TYPE(PyCapsule);
6776

6877

6978
extern void* to_java(PyObject* obj);

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,26 @@
3838
*/
3939
#include "capi.h"
4040

41+
PyTypeObject PyCapsule_Type = PY_TRUFFLE_TYPE("PyCapsule", &PyType_Type, 0);
42+
4143
PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor) {
42-
// TODO: fail for now
43-
return NULL;
44+
return (PyObject *)polyglot_as_PyCapsule(to_sulong(polyglot_invoke(PY_TRUFFLE_CEXT, "PyCapsule", polyglot_from_string(name, "ascii"), pointer, destructor)));
45+
}
46+
47+
void * PyCapsule_GetContext(PyObject *o) {
48+
void *result = polyglot_invoke(PY_TRUFFLE_CEXT, "PyCapsule_GetContext", to_java(o), ERROR_MARKER);
49+
if (result == ERROR_MARKER) {
50+
return NULL;
51+
}
52+
return (void *)as_long(result);
4453
}
54+
55+
void * PyCapsule_GetPointer(PyObject *o, const char *name) {
56+
void *result = polyglot_invoke(PY_TRUFFLE_CEXT, "PyCapsule_GetPointer", to_java(o), polyglot_from_string(name, "ascii"), ERROR_MARKER);
57+
if (result == ERROR_MARKER) {
58+
return NULL;
59+
}
60+
return (void *)as_long(result);
61+
}
62+
63+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates.
2+
#
3+
# The Universal Permissive License (UPL), Version 1.0
4+
#
5+
# Subject to the condition set forth below, permission is hereby granted to any
6+
# person obtaining a copy of this software, associated documentation and/or data
7+
# (collectively the "Software"), free of charge and under any and all copyright
8+
# rights in the Software, and any and all patent rights owned or freely
9+
# licensable by each licensor hereunder covering either (i) the unmodified
10+
# Software as contributed to or provided by such licensor, or (ii) the Larger
11+
# Works (as defined below), to deal in both
12+
#
13+
# (a) the Software, and
14+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15+
# one is included with the Software (each a "Larger Work" to which the
16+
# Software is contributed by such licensors),
17+
#
18+
# without restriction, including without limitation the rights to copy, create
19+
# derivative works of, display, perform, and distribute the Software and make,
20+
# use, sell, offer for sale, import, export, have made, and have sold the
21+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
22+
# either these or other terms.
23+
#
24+
# This license is subject to the following condition:
25+
#
26+
# The above copyright notice and either this complete permission notice or at a
27+
# minimum a reference to the UPL must be included in all copies or substantial
28+
# portions of the Software.
29+
#
30+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36+
# SOFTWARE.
37+
38+
import sys
39+
from . import CPyExtTestCase, CPyExtFunction, CPyExtFunctionOutVars, unhandled_error_compare, GRAALPYTHON
40+
__dir__ = __file__.rpartition("/")[0]
41+
42+
class TestPyCapsule(CPyExtTestCase):
43+
def compile_module(self, name):
44+
type(self).mro()[1].__dict__["test_%s" % name].create_module(name)
45+
super(TestPyCapsule, self).compile_module(name)
46+
47+
test_PyCapsule_CheckExact = CPyExtFunction(
48+
lambda args: True,
49+
lambda: (
50+
("hello",0xDEADBEEF),
51+
),
52+
code='''int wrap_PyCapsule_Check(char * name, Py_ssize_t ptr) {
53+
PyObject* capsule = PyCapsule_New(ptr, name, NULL);
54+
return PyCapsule_CheckExact(capsule);
55+
}
56+
''',
57+
resultspec="i",
58+
argspec='sn',
59+
arguments=["PyObject* name", "Py_ssize_t ptr"],
60+
callfunction="wrap_PyCapsule_Check",
61+
cmpfunc=unhandled_error_compare
62+
)

graalpython/lib-graalpython/python_cext.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,50 @@ def PyUnicode_Format(format, args, error_marker):
532532
return error_marker
533533

534534

535+
##################### CAPSULE
536+
537+
538+
class PyCapsule:
539+
name = None
540+
pointer = None
541+
context = None
542+
543+
def __init__(self, name, pointer, destructor):
544+
self.name = name
545+
self.pointer = pointer
546+
547+
def __repr__(self):
548+
name = "NULL" if self.name is None else self.name
549+
quote = "" if self.name is None else '"'
550+
return "<capsule object %s%s%s at %p>" % (quote, name, quote, self.pointer)
551+
552+
553+
def PyCapsule_GetContext(obj, error_marker):
554+
typ = val = tb = None
555+
try:
556+
if not isinstance(obj, PyCapsule) or obj.pointer is None:
557+
raise ValueError("PyCapsule_GetContext called with invalid PyCapsule object")
558+
return obj.context
559+
except BaseException:
560+
typ, val, tb = sys.exc_info()
561+
PyErr_Restore(typ, val, tb)
562+
return error_marker
563+
564+
565+
def PyCapsule_GetPointer(obj, name, error_marker):
566+
typ = val = tb = None
567+
try:
568+
if not isinstance(obj, PyCapsule) or obj.pointer is None:
569+
raise ValueError("PyCapsule_GetPointer called with invalid PyCapsule object")
570+
if name != obj.name:
571+
raise ValueError("PyCapsule_GetPointer called with incorrect name")
572+
return obj.pointer
573+
except BaseException:
574+
typ, val, tb = sys.exc_info()
575+
PyErr_Restore(typ, val, tb)
576+
return error_marker
577+
578+
535579
def PyModule_AddObject(m, k, v):
536580
m.__dict__[k] = v
537581
return None
@@ -996,6 +1040,8 @@ def PyTruffle_Type(type_name):
9961040
return type(sys)
9971041
elif type_name == "NoneType":
9981042
return type(None)
1043+
elif type_name == "PyCapsule":
1044+
return PyCapsule
9991045
else:
10001046
return getattr(sys.modules["builtins"], type_name)
10011047

0 commit comments

Comments
 (0)