Commit 745b1335 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge branch 'collections'

parents c4067082 b2a5d2b3
......@@ -124,7 +124,34 @@ class OrderedDict(dict):
for k in self:
yield (k, self[k])
update = MutableMapping.update
# Pyston change: copied the code in from _abcoll rather than calling "update = MutableMapping.update"
def update(*args, **kwds):
''' D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.
If E present and has a .keys() method, does: for k in E: D[k] = E[k]
If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v
In either case, this is followed by: for k, v in F.items(): D[k] = v
'''
if len(args) > 2:
raise TypeError("update() takes at most 2 positional "
"arguments ({} given)".format(len(args)))
elif not args:
raise TypeError("update() takes at least 1 argument (0 given)")
self = args[0]
other = args[1] if len(args) >= 2 else ()
# Pyston change: changed this from "Mapping" to "dict"
if isinstance(other, dict):
for key in other:
self[key] = other[key]
elif hasattr(other, "keys"):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
__update = update # let subclasses override update without breaking __init__
......
......@@ -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;
......
......@@ -52,7 +52,6 @@ void setupBool() {
bool_cls->giveAttr("__nonzero__", new BoxedFunction(boxRTFunction((void*)boolNonzero, BOXED_BOOL, 1)));
bool_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)boolRepr, STR, 1)));
bool_cls->giveAttr("__str__", bool_cls->getattr("__repr__"));
bool_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)boolHash, BOXED_INT, 1)));
bool_cls->giveAttr("__new__",
......
......@@ -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;
}
}
......@@ -1304,7 +1301,6 @@ void setupCAPI() {
capifunc_cls->giveAttr("__repr__",
new BoxedFunction(boxRTFunction((void*)BoxedCApiFunction::__repr__, UNKNOWN, 1)));
capifunc_cls->giveAttr("__str__", capifunc_cls->getattr("__repr__"));
capifunc_cls->giveAttr(
"__call__", new BoxedFunction(boxRTFunction((void*)BoxedCApiFunction::__call__, UNKNOWN, 1, 0, true, true)));
......
......@@ -48,13 +48,18 @@ Box* dictRepr(BoxedDict* self) {
}
Box* dictClear(BoxedDict* self) {
RELEASE_ASSERT(self->cls == dict_cls, "");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'clear' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
self->d.clear();
return None;
}
Box* dictCopy(BoxedDict* self) {
RELEASE_ASSERT(self->cls == dict_cls, "");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'copy' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
BoxedDict* r = new BoxedDict();
r->d.insert(self->d.begin(), self->d.end());
......@@ -91,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'",
......@@ -119,7 +138,10 @@ Box* dictViewItems(BoxedDict* self) {
}
Box* dictLen(BoxedDict* self) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__len__' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
return boxInt(self->d.size());
}
......@@ -143,8 +165,14 @@ 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) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__getitem__' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto it = self->d.find(k);
if (it == self->d.end()) {
......@@ -237,7 +265,9 @@ Box* dictSetitem(BoxedDict* self, Box* k, Box* v) {
}
Box* dictDelitem(BoxedDict* self, Box* k) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__delitem__' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto it = self->d.find(k);
if (it == self->d.end()) {
......@@ -255,7 +285,9 @@ Box* dictDelitem(BoxedDict* self, Box* k) {
}
Box* dictPop(BoxedDict* self, Box* k, Box* d) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'pop' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto it = self->d.find(k);
if (it == self->d.end()) {
......@@ -276,7 +308,9 @@ Box* dictPop(BoxedDict* self, Box* k, Box* d) {
}
Box* dictPopitem(BoxedDict* self) {
RELEASE_ASSERT(self->cls == dict_cls, "");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'popitem' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto it = self->d.begin();
if (it == self->d.end()) {
......@@ -292,7 +326,9 @@ Box* dictPopitem(BoxedDict* self) {
}
Box* dictGet(BoxedDict* self, Box* k, Box* d) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'get' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto it = self->d.find(k);
if (it == self->d.end())
......@@ -302,7 +338,9 @@ Box* dictGet(BoxedDict* self, Box* k, Box* d) {
}
Box* dictSetdefault(BoxedDict* self, Box* k, Box* v) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'setdefault' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto it = self->d.find(k);
if (it != self->d.end())
......@@ -313,7 +351,10 @@ Box* dictSetdefault(BoxedDict* self, Box* k, Box* v) {
}
Box* dictContains(BoxedDict* self, Box* k) {
assert(self->cls == dict_cls);
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor '__contains__' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
return boxBool(self->d.count(k) != 0);
}
......@@ -322,7 +363,9 @@ Box* dictNonzero(BoxedDict* self) {
}
Box* dictFromkeys(BoxedDict* self, Box* iterable, Box* default_value) {
RELEASE_ASSERT(self->cls == dict_cls, "");
if (!isSubclass(self->cls, dict_cls))
raiseExcHelper(TypeError, "descriptor 'fromkeys' requires a 'dict' object but received a '%s'",
getTypeName(self)->c_str());
auto rtn = new BoxedDict();
for (Box* e : iterable->pyElements()) {
......@@ -343,9 +386,7 @@ extern "C" Box* dictNew(Box* _cls, BoxedTuple* args, BoxedDict* kwargs) {
raiseExcHelper(TypeError, "dict.__new__(%s): %s is not a subtype of dict", getNameOfClass(cls)->c_str(),
getNameOfClass(cls)->c_str());
RELEASE_ASSERT(cls == dict_cls, "");
return new BoxedDict();
return new (cls) BoxedDict();
}
void dictMerge(BoxedDict* self, Box* other) {
......@@ -505,7 +546,6 @@ void setupDict() {
dict_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)dictNew, UNKNOWN, 1, 0, true, true)));
dict_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)dictInit, NONE, 1, 0, true, true)));
dict_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)dictRepr, STR, 1)));
dict_cls->giveAttr("__str__", dict_cls->getattr("__repr__"));
dict_cls->giveAttr("__iter__",
new BoxedFunction(boxRTFunction((void*)dictIterKeys, typeFromClass(dict_iterator_cls), 1)));
......
......@@ -291,7 +291,6 @@ void setupFile() {
file_cls->giveAttr("close", new BoxedFunction(boxRTFunction((void*)fileClose, NONE, 1)));
file_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)fileRepr, STR, 1)));
file_cls->giveAttr("__str__", file_cls->getattr("__repr__"));
file_cls->giveAttr("__enter__", new BoxedFunction(boxRTFunction((void*)fileEnter, typeFromClass(file_cls), 1)));
file_cls->giveAttr("__exit__", new BoxedFunction(boxRTFunction((void*)fileExit, UNKNOWN, 4)));
......
......@@ -876,7 +876,6 @@ void setupInt() {
int_cls->giveAttr("__neg__", new BoxedFunction(boxRTFunction((void*)intNeg, UNKNOWN, 1)));
int_cls->giveAttr("__nonzero__", new BoxedFunction(boxRTFunction((void*)intNonzero, BOXED_BOOL, 1)));
int_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)intRepr, STR, 1)));
int_cls->giveAttr("__str__", int_cls->getattr("__repr__"));
int_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)intHash, BOXED_INT, 1)));
int_cls->giveAttr("__divmod__", new BoxedFunction(boxRTFunction((void*)intDivmod, BOXED_TUPLE, 2)));
......
......@@ -670,7 +670,6 @@ void setupList() {
list_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)listNe, UNKNOWN, 2)));
list_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)listRepr, STR, 1)));
list_cls->giveAttr("__str__", list_cls->getattr("__repr__"));
list_cls->giveAttr("__nonzero__", new BoxedFunction(boxRTFunction((void*)listNonzero, BOXED_BOOL, 1)));
list_cls->giveAttr("pop", new BoxedFunction(boxRTFunction((void*)listPop, UNKNOWN, 2, 1, false, false), { None }));
......
......@@ -1463,12 +1463,6 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
#endif
}
if (strcmp(attr, "__dict__") == 0) {
// TODO this is wrong, should be added at the class level as a getset
if (obj->cls->instancesHaveHCAttrs())
return makeAttrWrapper(obj);
}
std::unique_ptr<Rewriter> rewriter(
Rewriter::createRewriter(__builtin_extract_return_addr(__builtin_return_address(0)), 2, "getattr"));
......@@ -1505,6 +1499,29 @@ extern "C" Box* getattr(Box* obj, const char* attr) {
if (val) {
return val;
}
if (attr[0] == '_' && attr[1] == '_') {
if (strcmp(attr, "__dict__") == 0) {
// TODO this is wrong, should be added at the class level as a getset
if (obj->cls->instancesHaveHCAttrs())
return makeAttrWrapper(obj);
}
// 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);
}
......@@ -1723,6 +1740,8 @@ extern "C" BoxedString* str(Box* obj) {
if (obj->cls != str_cls) {
static const std::string str_str("__str__");
// TODO could do an IC optimization here (once we do rewrites here at all):
// if __str__ is objectStr, just guard on that and call repr directly.
obj = callattrInternal(obj, &str_str, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
}
......@@ -2136,6 +2155,10 @@ extern "C" Box* callattr(Box* obj, const std::string* attr, CallattrFlags flags,
int num_orig_args = 4 + std::min(4, npassed_args);
if (argspec.num_keywords)
num_orig_args++;
// Uncomment this to help debug if callsites aren't getting rewritten:
// printf("Slowpath call: %p (%s.%s)\n", __builtin_return_address(0), obj->cls->tp_name, attr->c_str());
std::unique_ptr<Rewriter> rewriter(Rewriter::createRewriter(
__builtin_extract_return_addr(__builtin_return_address(0)), num_orig_args, "callattr"));
Box* rtn;
......
......@@ -412,7 +412,6 @@ void setupTuple() {
tuple_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)tupleHash, BOXED_INT, 1)));
tuple_cls->giveAttr("__len__", new BoxedFunction(boxRTFunction((void*)tupleLen, BOXED_INT, 1)));
tuple_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)tupleRepr, STR, 1)));
tuple_cls->giveAttr("__str__", tuple_cls->getattr("__repr__"));
tuple_cls->giveAttr("__add__", new BoxedFunction(boxRTFunction((void*)tupleAdd, BOXED_TUPLE, 2)));
tuple_cls->giveAttr("__mul__", new BoxedFunction(boxRTFunction((void*)tupleMul, BOXED_TUPLE, 2)));
tuple_cls->giveAttr("__rmul__", new BoxedFunction(boxRTFunction((void*)tupleMul, BOXED_TUPLE, 2)));
......
......@@ -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);
......@@ -1040,20 +1066,17 @@ void setupRuntime() {
type_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)typeNew, UNKNOWN, 4, 2, false, false), { NULL, NULL }));
type_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)typeRepr, STR, 1)));
type_cls->giveAttr("__str__", type_cls->getattr("__repr__"));
type_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)typeHash, BOXED_INT, 1)));
type_cls->freeze();
none_cls->giveAttr("__name__", boxStrConstant("NoneType"));
none_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)noneRepr, STR, 1)));
none_cls->giveAttr("__str__", none_cls->getattr("__repr__"));
none_cls->giveAttr("__hash__", new BoxedFunction(boxRTFunction((void*)noneHash, UNKNOWN, 1)));
none_cls->giveAttr("__nonzero__", new BoxedFunction(boxRTFunction((void*)noneNonzero, BOXED_BOOL, 1)));
none_cls->freeze();
module_cls->giveAttr("__name__", boxStrConstant("module"));
module_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)moduleRepr, STR, 1)));
module_cls->giveAttr("__str__", module_cls->getattr("__repr__"));
module_cls->freeze();
closure_cls->giveAttr("__name__", boxStrConstant("closure"));
......@@ -1079,7 +1102,6 @@ void setupRuntime() {
function_cls->giveAttr("__name__", boxStrConstant("function"));
function_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)functionRepr, STR, 1)));
function_cls->giveAttr("__str__", function_cls->getattr("__repr__"));
function_cls->giveAttr("__module__",
new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, offsetof(BoxedFunction, modname)));
function_cls->giveAttr("__get__", new BoxedFunction(boxRTFunction((void*)functionGet, UNKNOWN, 3)));
......@@ -1103,7 +1125,6 @@ void setupRuntime() {
slice_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)sliceNew, UNKNOWN, 4, 2, false, false), { NULL, None }));
slice_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)sliceRepr, STR, 1)));
slice_cls->giveAttr("__str__", slice_cls->getattr("__repr__"));
slice_cls->giveAttr("start", new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, SLICE_START_OFFSET));
slice_cls->giveAttr("stop", new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, SLICE_STOP_OFFSET));
slice_cls->giveAttr("step", new BoxedMemberDescriptor(BoxedMemberDescriptor::OBJECT, SLICE_STEP_OFFSET));
......@@ -1117,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();
......
......@@ -3,7 +3,8 @@
# but calling an attribute is such a common case that we special case it as a callattr(),
# which avoids the allocation/immediate deallocation of the instancemethod object.
# statcheck: noninit_count('num_instancemethods') <= 10
# statcheck: noninit_count('slowpath_callattr') <= 100
# statcheck: noninit_count('slowpath_callattr') <= 120
class C(object):
def foo(self, a0, a1, a2, a3, a4, a5, a6, a7):
print "foo", a0, a1, a2, a3, a4, a5, a6, a7
......
# skip-if: True
# - wip
# allow-warning: converting unicode literal to str
import collections
o = collections.OrderedDict()
print o.items()
for i in xrange(30):
o[(i ** 2) ^ 0xace] = i
print o
......@@ -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