Skip to content

Commit e9278f8

Browse files
committed
[GR-52707][GR-52721] Update torchvision
PullRequest: graalpython/3251
2 parents 9bceb00 + a3dc1b0 commit e9278f8

File tree

22 files changed

+544
-161
lines changed

22 files changed

+544
-161
lines changed

graalpython/com.oracle.graal.python.cext/include/internal/pycore_object.h

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2022, 2023, Oracle and/or its affiliates.
1+
/* Copyright (c) 2022, 2024, Oracle and/or its affiliates.
22
* Copyright (C) 1996-2022 Python Software Foundation
33
*
44
* Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
@@ -203,17 +203,14 @@ _PyObject_IS_GC(PyObject *obj)
203203
// Fast inlined version of PyType_IS_GC()
204204
#define _PyType_IS_GC(t) _PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
205205

206-
// GraalPy change: we don't want to pull in CPython GC implementation details
207-
/*
208206
static inline size_t
209207
_PyType_PreHeaderSize(PyTypeObject *tp)
210208
{
211-
return _PyType_IS_GC(tp) * sizeof(PyGC_Head) +
212-
_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT) * 2 * sizeof(PyObject *);
209+
// GraalPy change: remove CPython's GC header; also we put only one pointer for dict, we don't store it inlined
210+
return _PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT) * sizeof(PyObject *);
213211
}
214212

215213
void _PyObject_GC_Link(PyObject *op);
216-
*/
217214

218215
// Usage: assert(_Py_CheckSlotResult(obj, "__getitem__", result != NULL));
219216
extern int _Py_CheckSlotResult(
@@ -240,14 +237,16 @@ PyObject * _PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
240237

241238
static inline PyDictValues **_PyObject_ValuesPointer(PyObject *obj)
242239
{
243-
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
244-
return ((PyDictValues **)obj)-4;
240+
// GraalPy change: we don't have inlined managed dict values
241+
assert(0);
242+
return NULL;
245243
}
246244

247245
static inline PyObject **_PyObject_ManagedDictPointer(PyObject *obj)
248246
{
249247
assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
250-
return ((PyObject **)obj)-3;
248+
// GraalPy change: ours is at a different offset
249+
return ((PyObject **)obj)-1;
251250
}
252251

253252
#define MANAGED_DICT_OFFSET (((int)sizeof(PyObject *))*-3)

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

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2024, 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
@@ -40,6 +40,8 @@
4040
*/
4141
#include "capi.h"
4242

43+
#include "pycore_object.h" // _PyType_CheckConsistency(), _Py_FatalRefcountError()
44+
4345

4446
PyObject* _PyDict_NewPresized(Py_ssize_t minused) {
4547
/* we ignore requests to capacity for now */
@@ -110,53 +112,67 @@ int PyDict_DelItemString(PyObject *d, const char *key) {
110112
CALL_WITH_STRING(key, int, -1, GraalPyDict_DelItem, d, string);
111113
}
112114

113-
PyAPI_FUNC(PyObject*) _PyObject_GenericGetDict(PyObject* obj) {
114-
PyObject** dictptr = _PyObject_GetDictPtr(obj);
115+
PyObject *
116+
PyObject_GenericGetDict(PyObject *obj, void *context)
117+
{
118+
PyObject *dict;
119+
// GraalPy change: we don't have inlined values in managed dict
120+
PyObject **dictptr = _PyObject_GetDictPtr(obj);
115121
if (dictptr == NULL) {
122+
PyErr_SetString(PyExc_AttributeError,
123+
"This object has no __dict__");
116124
return NULL;
117125
}
118-
PyObject* dict = *dictptr;
126+
dict = *dictptr;
119127
if (dict == NULL) {
128+
// GraalPy change: we don't have CPython's cache
120129
*dictptr = dict = PyDict_New();
121130
}
122131
Py_XINCREF(dict);
123132
return dict;
124133
}
125134

126-
PyObject* PyObject_GenericGetDict(PyObject* obj, void* context) {
127-
PyObject* d = _PyObject_GenericGetDict(obj);
128-
if (d == NULL) {
129-
PyErr_SetString(PyExc_AttributeError, "This object has no __dict__");
130-
}
131-
return d;
132-
}
133-
134-
PyObject** _PyObject_GetDictPtr(PyObject* obj) {
135+
PyObject **
136+
_PyObject_DictPointer(PyObject *obj)
137+
{
135138
Py_ssize_t dictoffset;
136-
137-
// relies on the fact that 'tp_dictoffset' is in sync with the corresponding managed class !
138139
PyTypeObject *tp = Py_TYPE(obj);
139140

141+
if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
142+
return _PyObject_ManagedDictPointer(obj);
143+
}
140144
dictoffset = tp->tp_dictoffset;
141-
if (dictoffset == 0) {
145+
if (dictoffset == 0)
142146
return NULL;
143-
}
144147
if (dictoffset < 0) {
145-
Py_ssize_t nitems = ((PyVarObject *)obj)->ob_size;
146-
if (nitems < 0) {
147-
nitems = -nitems;
148+
Py_ssize_t tsize = Py_SIZE(obj);
149+
if (tsize < 0) {
150+
tsize = -tsize;
148151
}
152+
size_t size = _PyObject_VAR_SIZE(tp, tsize);
153+
assert(size <= (size_t)PY_SSIZE_T_MAX);
154+
dictoffset += (Py_ssize_t)size;
149155

150-
size_t size = tp->tp_basicsize + nitems * tp->tp_itemsize;
151-
if (size % SIZEOF_VOID_P != 0) {
152-
// round to full pointer boundary
153-
size += SIZEOF_VOID_P - (size % SIZEOF_VOID_P);
154-
}
155-
dictoffset += (long)size;
156+
_PyObject_ASSERT(obj, dictoffset > 0);
157+
_PyObject_ASSERT(obj, dictoffset % SIZEOF_VOID_P == 0);
156158
}
157159
return (PyObject **) ((char *)obj + dictoffset);
158160
}
159161

162+
/* Helper to get a pointer to an object's __dict__ slot, if any.
163+
* Creates the dict from inline attributes if necessary.
164+
* Does not set an exception.
165+
*
166+
* Note that the tp_dictoffset docs used to recommend this function,
167+
* so it should be treated as part of the public API.
168+
*/
169+
PyObject **
170+
_PyObject_GetDictPtr(PyObject *obj)
171+
{
172+
// GraalPy change: we don't have inlined managed dict values, so just use the common path
173+
return _PyObject_DictPointer(obj);
174+
}
175+
160176
/* Taken from CPython */
161177
int
162178
_PyDict_ContainsId(PyObject *op, struct _Py_Identifier *key)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -916,8 +916,9 @@ PyVarObject* _PyObject_GC_NewVar(PyTypeObject *tp, Py_ssize_t nitems) {
916916
return _PyObject_NewVar(tp, nitems);
917917
}
918918

919-
void PyObject_GC_Del(void *tp) {
920-
PyObject_Free(tp);
919+
void PyObject_GC_Del(void *op) {
920+
size_t presize = _PyType_PreHeaderSize(((PyObject *)op)->ob_type);
921+
PyObject_Free(((char *)op)-presize);
921922
}
922923

923924
void PyObject_GC_Track(void* a) {

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,12 +1118,16 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems)
11181118
const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
11191119
/* note that we need to add one, for the sentinel */
11201120

1121-
// GraalPy change: remove the GC header
1122-
char *alloc = PyObject_Malloc(size);
1121+
const size_t presize = _PyType_PreHeaderSize(type);
1122+
char *alloc = PyObject_Malloc(size + presize);
11231123
if (alloc == NULL) {
11241124
return PyErr_NoMemory();
11251125
}
1126-
obj = (PyObject *)alloc;
1126+
obj = (PyObject *)(alloc + presize);
1127+
if (presize) {
1128+
// GraalPy change: different header layout, no GC link
1129+
((PyObject **)alloc)[0] = NULL;
1130+
}
11271131
memset(obj, '\0', size);
11281132

11291133
if (type->tp_itemsize == 0) {

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,13 +754,18 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
754754
755755
{includes}
756756
757+
typedef struct {{
758+
{struct_base}
759+
{cmembers}
760+
}} {name}Object;
761+
757762
{code}
758763
759764
PyType_Slot slots[] = {{
760765
{slots}
761766
}};
762767
763-
PyType_Spec spec = {{ "{name}Type", sizeof(PyHeapTypeObject), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }};
768+
PyType_Spec spec = {{ "{name}Type", sizeof({name}Object), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }};
764769
765770
static PyObject* create(PyObject* unused, PyObject* bases) {{
766771
{ready_code}
@@ -798,6 +803,8 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
798803
kwargs["code"] = code
799804
kwargs["slots"] = '{0}' if slots is None else ',\n'.join(slots + ['{0}'])
800805
kwargs.setdefault("includes", "")
806+
kwargs.setdefault("struct_base", "PyObject_HEAD")
807+
kwargs.setdefault("cmembers", "")
801808
kwargs.setdefault("ready_code", "")
802809
kwargs.setdefault("post_ready_code", "")
803810
code = UnseenFormatter().format(template, **kwargs)
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
from tests.cpyext import CPyExtType, CPyExtHeapType, assert_raises
41+
42+
NativeTypeWithoutDict = CPyExtType(
43+
name='NativeTypeWithoutDict',
44+
)
45+
46+
NativeTypeWithDict = CPyExtType(
47+
name='NativeTypeWithDict',
48+
cmembers='PyObject* dict;',
49+
tp_dictoffset='offsetof(NativeTypeWithDictObject, dict)',
50+
tp_getset='{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}',
51+
)
52+
53+
NativeHeapTypeWithoutDict = CPyExtHeapType(
54+
name='NativeTypeWithManagedDict',
55+
)
56+
57+
NativeHeapTypeWithDict = CPyExtHeapType(
58+
name='NativeHeapTypeWithManagedDict',
59+
cmembers='PyObject* dict;',
60+
code='''
61+
static PyMemberDef memberlist[] = {
62+
{"__dictoffset__", T_PYSSIZET, offsetof(NativeHeapTypeWithManagedDictObject, dict), READONLY},
63+
{NULL}
64+
};
65+
static PyGetSetDef getsetlist[] = {
66+
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
67+
{NULL}
68+
};
69+
''',
70+
slots=[
71+
'{Py_tp_members, memberlist}',
72+
'{Py_tp_getset, getsetlist}',
73+
],
74+
)
75+
76+
77+
# TODO it would be great if we could test creating heap types with Py_TPFLAGS_MANAGED_DICT, because pybind11 does that,
78+
# but there's no way to do that without abusing abusing implementations details
79+
80+
81+
class NativeSubtypeWithDict(NativeTypeWithDict):
82+
pass
83+
84+
85+
class NativeSubtypeWithAddedDict(NativeTypeWithoutDict):
86+
pass
87+
88+
89+
class TestObjectDict:
90+
@staticmethod
91+
def assert_has_no_dict(obj):
92+
assert_raises(AttributeError, setattr, obj, 'foo', 1)
93+
assert_raises(AttributeError, getattr, obj, '__dict__')
94+
95+
@staticmethod
96+
def assert_has_working_dict(obj):
97+
obj.foo = 1
98+
assert obj.foo == 1
99+
assert obj.__dict__ == {'foo': 1}
100+
101+
def test_no_dict(self):
102+
self.assert_has_no_dict(NativeTypeWithoutDict())
103+
self.assert_has_no_dict(NativeHeapTypeWithoutDict())
104+
105+
def test_dict(self):
106+
self.assert_has_working_dict(NativeTypeWithDict())
107+
self.assert_has_working_dict(NativeHeapTypeWithDict())
108+
self.assert_has_working_dict(NativeSubtypeWithDict())
109+
self.assert_has_working_dict(NativeSubtypeWithAddedDict())

graalpython/com.oracle.graal.python.test/src/tests/package/moduleA.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2024, 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
@@ -40,3 +40,6 @@
4040
"""MODULE A DOC"""
4141

4242
print("module A")
43+
44+
publicProperty = 1
45+
_privateProperty = 2

graalpython/com.oracle.graal.python.test/src/tests/package/moduleY.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2024, 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
@@ -41,3 +41,8 @@
4141
import math
4242
print("cos(100) = "+str(math.cos(100)))
4343
from .subpackage1 import moduleX
44+
45+
__all__ = ['publicProperty', '_privateProperty']
46+
publicProperty = 1
47+
_privateProperty = 2
48+
unlistedProperty = 3

graalpython/com.oracle.graal.python.test/src/tests/test_imports.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2024, 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
@@ -93,6 +93,21 @@ def test_import_star_has_to_be_module():
9393
assert False
9494

9595

96+
def test_import_star_no_all():
97+
g = {}
98+
exec("from package.moduleA import *", g)
99+
assert 'publicProperty' in g
100+
assert '_privateProperty' not in g
101+
102+
103+
def test_import_star_all():
104+
g = {}
105+
exec("from package.moduleY import *", g)
106+
assert 'publicProperty' in g
107+
assert '_privateProperty' in g
108+
assert 'unlistedProperty' not in g
109+
110+
96111
def test_import_as_local():
97112
from sys import path
98113
from sys import path as mypath

0 commit comments

Comments
 (0)