Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
Pyston
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
Pyston
Commits
0235e191
Commit
0235e191
authored
Mar 17, 2015
by
Kevin Modzelewski
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #365 from toshok/threading.local
__setattr_ and thread._local
parents
1a6b1e0c
18624ecc
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
226 additions
and
5 deletions
+226
-5
from_cpython/Include/object.h
from_cpython/Include/object.h
+2
-2
src/capi/object.cpp
src/capi/object.cpp
+7
-1
src/capi/typeobject.cpp
src/capi/typeobject.cpp
+67
-2
src/runtime/builtin_modules/thread.cpp
src/runtime/builtin_modules/thread.cpp
+31
-0
src/runtime/capi.cpp
src/runtime/capi.cpp
+10
-0
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+28
-0
test/test_extension/slots_test.c
test/test_extension/slots_test.c
+10
-0
test/tests/capi_slots.py
test/tests/capi_slots.py
+50
-0
test/tests/setattr.py
test/tests/setattr.py
+7
-0
test/tests/threading_local.py
test/tests/threading_local.py
+14
-0
No files found.
from_cpython/Include/object.h
View file @
0235e191
...
...
@@ -560,10 +560,10 @@ extern int _PyObject_SlotCompare(PyObject *, PyObject *);
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
dict as the last parameter. */
PyAPI_FUNC
(
PyObject
*
)
_PyObject_GenericGetAttrWithDict
(
PyObject
*
,
PyObject
*
,
PyObject
*
);
_PyObject_GenericGetAttrWithDict
(
PyObject
*
,
PyObject
*
,
PyObject
*
)
PYSTON_NOEXCEPT
;
PyAPI_FUNC
(
int
)
_PyObject_GenericSetAttrWithDict
(
PyObject
*
,
PyObject
*
,
PyObject
*
,
PyObject
*
);
PyObject
*
,
PyObject
*
)
PYSTON_NOEXCEPT
;
/* PyObject_Dir(obj) acts like Python __builtin__.dir(obj), returning a
...
...
src/capi/object.cpp
View file @
0235e191
...
...
@@ -144,7 +144,13 @@ extern "C" PyObject* PyObject_SelfIter(PyObject* obj) noexcept {
}
extern
"C"
int
PyObject_GenericSetAttr
(
PyObject
*
obj
,
PyObject
*
name
,
PyObject
*
value
)
noexcept
{
Py_FatalError
(
"unimplemented"
);
try
{
setattr
(
obj
,
static_cast
<
BoxedString
*>
(
name
)
->
s
.
c_str
(),
value
);
}
catch
(
ExcInfo
e
)
{
setCAPIException
(
e
);
return
-
1
;
}
return
0
;
}
extern
"C"
int
PyObject_SetAttr
(
PyObject
*
v
,
PyObject
*
name
,
PyObject
*
value
)
noexcept
{
...
...
src/capi/typeobject.cpp
View file @
0235e191
...
...
@@ -41,6 +41,54 @@ static int check_num_args(PyObject* ob, int n) noexcept {
return
0
;
}
/* Helper to check for object.__setattr__ or __delattr__ applied to a type.
This is called the Carlo Verre hack after its discoverer. */
static
int
hackcheck
(
PyObject
*
self
,
setattrofunc
func
,
const
char
*
what
)
noexcept
{
PyTypeObject
*
type
=
Py_TYPE
(
self
);
while
(
type
&&
type
->
tp_flags
&
Py_TPFLAGS_HEAPTYPE
)
type
=
type
->
tp_base
;
/* If type is NULL now, this is a really weird type.
In the spirit of backwards compatibility (?), just shut up. */
if
(
type
&&
type
->
tp_setattro
!=
func
)
{
PyErr_Format
(
PyExc_TypeError
,
"can't apply this %s to %s object"
,
what
,
type
->
tp_name
);
return
0
;
}
return
1
;
}
static
PyObject
*
wrap_setattr
(
PyObject
*
self
,
PyObject
*
args
,
void
*
wrapped
)
noexcept
{
setattrofunc
func
=
(
setattrofunc
)
wrapped
;
int
res
;
PyObject
*
name
,
*
value
;
if
(
!
PyArg_UnpackTuple
(
args
,
""
,
2
,
2
,
&
name
,
&
value
))
return
NULL
;
if
(
!
hackcheck
(
self
,
func
,
"__setattr__"
))
return
NULL
;
res
=
(
*
func
)(
self
,
name
,
value
);
if
(
res
<
0
)
return
NULL
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
wrap_delattr
(
PyObject
*
self
,
PyObject
*
args
,
void
*
wrapped
)
noexcept
{
setattrofunc
func
=
(
setattrofunc
)
wrapped
;
int
res
;
PyObject
*
name
;
if
(
!
check_num_args
(
args
,
1
))
return
NULL
;
name
=
PyTuple_GET_ITEM
(
args
,
0
);
if
(
!
hackcheck
(
self
,
func
,
"__delattr__"
))
return
NULL
;
res
=
(
*
func
)(
self
,
name
,
NULL
);
if
(
res
<
0
)
return
NULL
;
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
wrap_hashfunc
(
PyObject
*
self
,
PyObject
*
args
,
void
*
wrapped
)
noexcept
{
hashfunc
func
=
(
hashfunc
)
wrapped
;
long
res
;
...
...
@@ -633,6 +681,20 @@ static PyObject* slot_tp_getattro(PyObject* self, PyObject* name) noexcept {
return
call_method
(
self
,
"__getattribute__"
,
&
getattribute_str
,
"(O)"
,
name
);
}
static
int
slot_tp_setattro
(
PyObject
*
self
,
PyObject
*
name
,
PyObject
*
value
)
noexcept
{
PyObject
*
res
;
static
PyObject
*
delattr_str
,
*
setattr_str
;
if
(
value
==
NULL
)
res
=
call_method
(
self
,
"__delattr__"
,
&
delattr_str
,
"(O)"
,
name
);
else
res
=
call_method
(
self
,
"__setattr__"
,
&
setattr_str
,
"(OO)"
,
name
,
value
);
if
(
res
==
NULL
)
return
-
1
;
Py_DECREF
(
res
);
return
0
;
}
static
PyObject
*
call_attribute
(
PyObject
*
self
,
PyObject
*
attr
,
PyObject
*
name
)
noexcept
{
PyObject
*
res
,
*
descr
=
NULL
;
descrgetfunc
f
=
Py_TYPE
(
attr
)
->
tp_descr_get
;
...
...
@@ -1136,6 +1198,8 @@ static void** slotptr(BoxedClass* type, int offset) noexcept {
static
slotdef
slotdefs
[]
=
{
TPSLOT
(
"__getattr__"
,
tp_getattr
,
NULL
,
NULL
,
""
),
TPSLOT
(
"__setattr__"
,
tp_setattr
,
NULL
,
NULL
,
""
),
TPSLOT
(
"__delattr__"
,
tp_setattr
,
NULL
,
NULL
,
""
),
TPSLOT
(
"__repr__"
,
tp_repr
,
slot_tp_repr
,
wrap_unaryfunc
,
"x.__repr__() <==> repr(x)"
),
TPSLOT
(
"__hash__"
,
tp_hash
,
slot_tp_hash
,
wrap_hashfunc
,
"x.__hash__() <==> hash(x)"
),
...
...
@@ -1144,6 +1208,9 @@ static slotdef slotdefs[]
TPSLOT
(
"__str__"
,
tp_str
,
slot_tp_str
,
wrap_unaryfunc
,
"x.__str__() <==> str(x)"
),
TPSLOT
(
"__getattr__"
,
tp_getattro
,
slot_tp_getattr_hook
,
NULL
,
""
),
TPSLOT
(
"__setattr__"
,
tp_setattro
,
slot_tp_setattro
,
wrap_setattr
,
"x.__setattr__('name', value) <==> x.name = value"
),
TPSLOT
(
"__delattr__"
,
tp_setattro
,
slot_tp_setattro
,
wrap_delattr
,
"x.__delattr__('name') <==> del x.name"
),
TPSLOT
(
"__lt__"
,
tp_richcompare
,
slot_tp_richcompare
,
richcmp_lt
,
"x.__lt__(y) <==> x<y"
),
TPSLOT
(
"__le__"
,
tp_richcompare
,
slot_tp_richcompare
,
richcmp_le
,
"x.__le__(y) <==> x<=y"
),
...
...
@@ -2308,11 +2375,9 @@ extern "C" int PyType_Ready(PyTypeObject* cls) noexcept {
gc
::
registerNonheapRootObject
(
cls
);
// unhandled fields:
RELEASE_ASSERT
(
cls
->
tp_setattr
==
NULL
,
""
);
RELEASE_ASSERT
(
cls
->
tp_compare
==
NULL
,
""
);
RELEASE_ASSERT
(
cls
->
tp_getattro
==
NULL
||
cls
->
tp_getattro
==
PyObject_GenericGetAttr
,
""
);
RELEASE_ASSERT
(
cls
->
tp_setattro
==
NULL
||
cls
->
tp_setattro
==
PyObject_GenericSetAttr
,
""
);
int
ALLOWABLE_FLAGS
=
Py_TPFLAGS_DEFAULT
|
Py_TPFLAGS_BASETYPE
|
Py_TPFLAGS_HAVE_GC
|
Py_TPFLAGS_CHECKTYPES
|
Py_TPFLAGS_HAVE_NEWBUFFER
;
...
...
src/runtime/builtin_modules/thread.cpp
View file @
0235e191
...
...
@@ -155,6 +155,32 @@ class BoxedThreadLocal : public Box {
public:
BoxedThreadLocal
()
{}
static
Box
*
getThreadLocalObject
(
Box
*
obj
)
{
BoxedDict
*
dict
=
static_cast
<
BoxedDict
*>
(
PyThreadState_GetDict
());
Box
*
tls_obj
=
dict
->
getOrNull
(
obj
);
if
(
tls_obj
==
NULL
)
{
tls_obj
=
new
BoxedDict
();
setitem
(
dict
,
obj
,
tls_obj
);
}
return
tls_obj
;
}
static
int
setattr
(
Box
*
obj
,
char
*
name
,
Box
*
val
)
{
Box
*
tls_obj
=
getThreadLocalObject
(
obj
);
setitem
(
tls_obj
,
boxString
(
name
),
val
);
return
0
;
}
static
Box
*
getattr
(
Box
*
obj
,
char
*
name
)
{
Box
*
tls_obj
=
getThreadLocalObject
(
obj
);
if
(
!
strcmp
(
name
,
"__dict__"
))
return
tls_obj
;
return
getitem
(
tls_obj
,
boxString
(
name
));
}
static
Box
*
hash
(
Box
*
obj
)
{
return
boxInt
(
PyThread_get_thread_ident
());
}
DEFAULT_CLASS
(
thread_local_cls
);
};
...
...
@@ -194,9 +220,14 @@ void setupThread() {
thread_local_cls
=
BoxedHeapClass
::
create
(
type_cls
,
object_cls
,
NULL
,
0
,
0
,
sizeof
(
BoxedThreadLocal
),
false
,
"_local"
);
thread_local_cls
->
giveAttr
(
"__module__"
,
boxStrConstant
(
"thread"
));
thread_local_cls
->
giveAttr
(
"__hash__"
,
new
BoxedFunction
(
boxRTFunction
((
void
*
)
BoxedThreadLocal
::
hash
,
BOXED_INT
,
1
)));
thread_local_cls
->
freeze
();
thread_module
->
giveAttr
(
"_local"
,
thread_local_cls
);
thread_local_cls
->
tp_setattr
=
BoxedThreadLocal
::
setattr
;
thread_local_cls
->
tp_getattr
=
BoxedThreadLocal
::
getattr
;
BoxedClass
*
ThreadError
=
BoxedHeapClass
::
create
(
type_cls
,
Exception
,
NULL
,
Exception
->
attrs_offset
,
Exception
->
tp_weaklistoffset
,
Exception
->
tp_basicsize
,
false
,
"error"
);
...
...
src/runtime/capi.cpp
View file @
0235e191
...
...
@@ -272,6 +272,16 @@ extern "C" PyObject* PyObject_GenericGetAttr(PyObject* o, PyObject* name) noexce
}
}
extern
"C"
PyObject
*
_PyObject_GenericGetAttrWithDict
(
PyObject
*
obj
,
PyObject
*
name
,
PyObject
*
dict
)
noexcept
{
Py_FatalError
(
"unimplemented"
);
}
extern
"C"
int
_PyObject_GenericSetAttrWithDict
(
PyObject
*
obj
,
PyObject
*
name
,
PyObject
*
value
,
PyObject
*
dict
)
noexcept
{
Py_FatalError
(
"unimplemented"
);
}
extern
"C"
PyObject
*
PyObject_GetItem
(
PyObject
*
o
,
PyObject
*
key
)
noexcept
{
try
{
return
getitem
(
o
,
key
);
...
...
src/runtime/objmodel.cpp
View file @
0235e191
...
...
@@ -81,6 +81,7 @@ static const std::string iter_str("__iter__");
static
const
std
::
string
new_str
(
"__new__"
);
static
const
std
::
string
none_str
(
"None"
);
static
const
std
::
string
repr_str
(
"__repr__"
);
static
const
std
::
string
setattr_str
(
"__setattr__"
);
static
const
std
::
string
setitem_str
(
"__setitem__"
);
static
const
std
::
string
set_str
(
"__set__"
);
static
const
std
::
string
str_str
(
"__str__"
);
...
...
@@ -1709,8 +1710,35 @@ void setattrInternal(Box* obj, const std::string& attr, Box* val, SetattrRewrite
// We don't need to to the invalidation stuff in this case.
return
;
}
else
{
// Finally, check __setattr__
if
(
obj
->
cls
->
tp_setattr
)
{
rewrite_args
=
NULL
;
REWRITE_ABORTED
(
""
);
int
rtn
=
obj
->
cls
->
tp_setattr
(
obj
,
const_cast
<
char
*>
(
attr
.
c_str
()),
val
);
if
(
rtn
)
throwCAPIException
();
return
;
}
Box
*
setattr
=
typeLookup
(
obj
->
cls
,
setattr_str
,
NULL
);
if
(
setattr
)
{
rewrite_args
=
NULL
;
REWRITE_ABORTED
(
""
);
// if we're dealing with a BoxedWrapperDescriptor wrapping
// PyObject_GenericSetAttr, skip calling __setattr__, as
// that will just re-enter us.
if
(
setattr
->
cls
!=
wrapperdescr_cls
||
((
BoxedWrapperDescriptor
*
)
setattr
)
->
wrapped
!=
PyObject_GenericSetAttr
)
{
Box
*
boxstr
=
boxString
(
attr
);
runtimeCallInternal
(
setattr
,
NULL
,
ArgPassSpec
(
3
),
obj
,
boxstr
,
val
,
NULL
,
NULL
);
return
;
}
}
if
(
!
obj
->
cls
->
instancesHaveHCAttrs
()
&&
!
obj
->
cls
->
instancesHaveDictAttrs
())
raiseAttributeError
(
obj
,
attr
.
c_str
());
obj
->
setattr
(
attr
,
val
,
rewrite_args
);
}
...
...
test/test_extension/slots_test.c
View file @
0235e191
...
...
@@ -626,6 +626,16 @@ call_funcs(PyObject* _module, PyObject* args) {
printf
(
"tp_getattr doesnt exist
\n
"
);
}
// we aren't checking for tp_getattro. it's set in cpython and not in pyston
if
(
cls
->
tp_setattr
)
{
printf
(
"tp_setattr exists
\n
"
);
}
else
{
printf
(
"tp_setattr doesnt exist
\n
"
);
}
// we aren't checking for tp_setattro. it's set in cpython and not in pyston
if
(
cls
->
tp_as_mapping
)
{
printf
(
"tp_as_mapping exists
\n
"
);
PyMappingMethods
*
map
=
cls
->
tp_as_mapping
;
...
...
test/tests/capi_slots.py
View file @
0235e191
...
...
@@ -161,3 +161,53 @@ try:
print
c
.
foo
()
except
SystemError
,
e
:
print
e
print
"**** setattr on class def"
class
C7
(
C
):
def
__setattr__
(
self
,
attr
,
val
):
print
"setattr"
,
attr
,
val
c
=
C7
()
slots_test
.
call_funcs
(
c
)
c
.
foo
=
1
c
.
bar
=
2
c
.
baz
=
3
print
"**** setattr set after the fact"
def
_setattr_
(
self
,
attr
,
val
):
print
"_setattr_"
,
attr
,
val
c
=
C6
()
c
.
__setattr__
=
_setattr_
slots_test
.
call_funcs
(
c
)
c
.
foo
=
1
c
.
bar
=
2
c
.
baz
=
3
print
"**** delattr on class def"
class
C8
(
C
):
def
__delattr__
(
self
,
attr
):
print
"delattr"
,
attr
c
=
C8
()
slots_test
.
call_funcs
(
c
)
delattr
(
c
,
'foo'
)
del
c
.
bar
print
"**** delattr set after the fact"
def
_delattr_
(
self
,
attr
):
print
"_delattr_"
,
attr
c
=
C6
()
c
.
__delattr__
=
_delattr_
slots_test
.
call_funcs
(
c
)
try
:
delattr
(
c
,
'foo'
)
except
Exception
as
e
:
pass
try
:
del
c
.
bar
except
Exception
as
e
:
pass
test/tests/setattr.py
0 → 100644
View file @
0235e191
class
Test
(
object
):
def
__setattr__
(
self
,
name
,
val
):
print
name
,
val
t
=
Test
()
t
.
hello
=
"world"
test/tests/threading_local.py
0 → 100644
View file @
0235e191
import
threading
a
=
threading
.
local
()
a
.
x
=
"hello world"
def
f
():
a
.
x
=
"goodbye world"
print
a
.
x
thread
=
threading
.
Thread
(
target
=
f
)
thread
.
start
()
thread
.
join
()
print
a
.
x
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment