Commit 0235e191 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #365 from toshok/threading.local

__setattr_ and thread._local
parents 1a6b1e0c 18624ecc
......@@ -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
......
......@@ -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 {
......
......@@ -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;
......
......@@ -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");
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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
class Test(object):
def __setattr__(self, name, val):
print name, val
t = Test()
t.hello = "world"
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment