Commit b68b8da7 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add support for dir looking at a class's bases

parent 6abd5698
......@@ -374,6 +374,22 @@ extern "C" int PySequence_Check(PyObject* s) noexcept {
return s->cls->tp_as_sequence && s->cls->tp_as_sequence->sq_item != NULL;
}
extern "C" Py_ssize_t PySequence_Size(PyObject* s) noexcept {
PySequenceMethods* m;
if (s == NULL) {
null_error();
return -1;
}
m = s->cls->tp_as_sequence;
if (m && m->sq_length)
return m->sq_length(s);
type_error("object of type '%.200s' has no len()", s);
return -1;
}
static PyObject* binary_op1(PyObject* v, PyObject* w, const int op_slot) {
PyObject* x;
binaryfunc slotv = NULL;
......
......@@ -58,6 +58,77 @@ extern "C" Box* trap() {
return None;
}
/* Helper for PyObject_Dir.
Merge the __dict__ of aclass into dict, and recursively also all
the __dict__s of aclass's base classes. The order of merging isn't
defined, as it's expected that only the final set of dict keys is
interesting.
Return 0 on success, -1 on error.
*/
static int merge_class_dict(PyObject* dict, PyObject* aclass) {
PyObject* classdict;
PyObject* bases;
assert(PyDict_Check(dict));
assert(aclass);
/* Merge in the type's dict (if any). */
classdict = PyObject_GetAttrString(aclass, "__dict__");
if (classdict == NULL)
PyErr_Clear();
else {
int status = PyDict_Update(dict, classdict);
Py_DECREF(classdict);
if (status < 0)
return -1;
}
/* Recursively merge in the base types' (if any) dicts. */
bases = PyObject_GetAttrString(aclass, "__bases__");
if (bases == NULL)
PyErr_Clear();
else {
/* We have no guarantee that bases is a real tuple */
Py_ssize_t i, n;
n = PySequence_Size(bases); /* This better be right */
if (n < 0)
PyErr_Clear();
else {
for (i = 0; i < n; i++) {
int status;
PyObject* base = PySequence_GetItem(bases, i);
if (base == NULL) {
Py_DECREF(bases);
return -1;
}
status = merge_class_dict(dict, base);
Py_DECREF(base);
if (status < 0) {
Py_DECREF(bases);
return -1;
}
}
}
Py_DECREF(bases);
}
return 0;
}
/* Helper for PyObject_Dir of type objects: returns __dict__ and __bases__.
We deliberately don't suck up its __class__, as methods belonging to the
metaclass would probably be more confusing than helpful.
*/
static PyObject* _specialized_dir_type(PyObject* obj) {
PyObject* result = NULL;
PyObject* dict = PyDict_New();
if (dict != NULL && merge_class_dict(dict, obj) == 0)
result = PyDict_Keys(dict);
Py_XDECREF(dict);
return result;
}
extern "C" Box* dir(Box* obj) {
if (obj == NULL) {
// TODO: This should actually return the elements in the current local
......@@ -76,6 +147,13 @@ extern "C" Box* dir(Box* obj) {
return dir_result;
}
if (isSubclass(obj->cls, type_cls)) {
Box* r = _specialized_dir_type(obj);
checkAndThrowCAPIException();
assert(r);
return r;
}
// If __dict__ is present use its keys and add the reset below
Box* obj_dict = getattrInternal(obj, "__dict__", nullptr);
if (obj_dict && obj_dict->cls == dict_cls) {
......
......@@ -476,10 +476,6 @@ extern "C" int PyObject_Print(PyObject* obj, FILE* fp, int flags) noexcept {
Py_FatalError("unimplemented");
};
extern "C" Py_ssize_t PySequence_Size(PyObject* o) noexcept {
Py_FatalError("unimplemented");
}
extern "C" PyObject* PySequence_Repeat(PyObject* o, Py_ssize_t count) noexcept {
Py_FatalError("unimplemented");
}
......@@ -497,7 +493,8 @@ extern "C" PyObject* PySequence_GetItem(PyObject* o, Py_ssize_t i) noexcept {
// Not sure if this is really the same:
return getitem(o, boxInt(i));
} catch (ExcInfo e) {
Py_FatalError("unimplemented");
setCAPIException(e);
return NULL;
}
}
......
......@@ -96,6 +96,20 @@ Box* dictKeys(BoxedDict* self) {
return rtn;
}
extern "C" PyObject* PyDict_Keys(PyObject* mp) noexcept {
if (mp == NULL || !PyDict_Check(mp)) {
PyErr_BadInternalCall();
return NULL;
}
try {
return dictKeys(static_cast<BoxedDict*>(mp));
} catch (ExcInfo e) {
setCAPIException(e);
return NULL;
}
}
Box* dictViewKeys(BoxedDict* self) {
if (!isSubclass(self->cls, dict_cls)) {
raiseExcHelper(TypeError, "descriptor 'viewkeys' requires a 'dict' object but received a '%s'",
......@@ -151,6 +165,10 @@ extern "C" PyObject* PyDict_Copy(PyObject* o) noexcept {
}
}
extern "C" int PyDict_Update(PyObject* a, PyObject* b) noexcept {
return PyDict_Merge(a, b, 1);
}
Box* dictGetitem(BoxedDict* self, Box* k) {
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__getitem__' requires a 'dict' object but received a '%s'",
......
......@@ -1507,11 +1507,19 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
return makeAttrWrapper(obj);
}
// I'm not sure if there's more to it than this:
// There's more to it than this:
if (strcmp(attr, "__class__") == 0) {
assert(obj->cls != instance_cls); // I think in this case __class__ is supposed to be the classobj?
return obj->cls;
}
// This doesn't belong here either:
if (strcmp(attr, "__bases__") == 0 && isSubclass(obj->cls, type_cls)) {
BoxedClass* cls = static_cast<BoxedClass*>(obj);
if (cls->tp_base)
return new BoxedTuple({ static_cast<BoxedClass*>(obj)->tp_base });
return EmptyTuple;
}
}
raiseAttributeError(obj, attr);
......
......@@ -860,6 +860,32 @@ public:
return r ? True : False;
}
static Box* keys(Box* _self) {
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
BoxedList* rtn = new BoxedList();
HCAttrs* attrs = self->b->getHCAttrsPtr();
for (const auto& p : attrs->hcls->attr_offsets) {
listAppend(rtn, boxString(p.first));
}
return rtn;
}
static Box* values(Box* _self) {
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
BoxedList* rtn = new BoxedList();
HCAttrs* attrs = self->b->getHCAttrsPtr();
for (const auto& p : attrs->hcls->attr_offsets) {
listAppend(rtn, attrs->attr_list->attrs[p.second]);
}
return rtn;
}
static Box* items(Box* _self) {
RELEASE_ASSERT(_self->cls == attrwrapper_cls, "");
AttrWrapper* self = static_cast<AttrWrapper*>(_self);
......@@ -1112,6 +1138,8 @@ void setupRuntime() {
attrwrapper_cls->giveAttr("__str__", new BoxedFunction(boxRTFunction((void*)AttrWrapper::str, UNKNOWN, 1)));
attrwrapper_cls->giveAttr("__contains__",
new BoxedFunction(boxRTFunction((void*)AttrWrapper::contains, UNKNOWN, 2)));
attrwrapper_cls->giveAttr("keys", new BoxedFunction(boxRTFunction((void*)AttrWrapper::keys, LIST, 1)));
attrwrapper_cls->giveAttr("values", new BoxedFunction(boxRTFunction((void*)AttrWrapper::values, LIST, 1)));
attrwrapper_cls->giveAttr("items", new BoxedFunction(boxRTFunction((void*)AttrWrapper::items, LIST, 1)));
attrwrapper_cls->freeze();
......
......@@ -69,3 +69,11 @@ print len(fake()) == len(dir(TestClass()))
for t in [str, int, list, set, dict]:
test_in_dir(['__str__', '__new__', '__repr__', '__dir__', '__module__'], t)
class C1(object):
a = 1
b = 2
class C2(C1):
b = 3
c = 4
print sorted([s for s in dir(C2) if s[0] != '_'])
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