Commit d88d20e8 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #921 from Daetalus/test_set

Some stuff to enable "test_set"
parents a419290b 7dc0be45
# expected: fail
import unittest
from test import test_support
import gc
......@@ -339,7 +337,9 @@ class TestJointOps(unittest.TestCase):
obj.x = iter(container)
del obj, container
gc.collect()
self.assertTrue(ref() is None, "Cycle was not collected")
# Pyston change: because with conservative scanning
# it is hard to guarantee finalizer calls
# self.assertTrue(ref() is None, "Cycle was not collected")
class TestSet(TestJointOps):
thetype = set
......@@ -560,7 +560,9 @@ class TestSet(TestJointOps):
p = weakref.proxy(s)
self.assertEqual(str(p), str(s))
s = None
self.assertRaises(ReferenceError, str, p)
# Pyston change: because with conservative scanning
# it is hard to guarantee finalizer calls
# self.assertRaises(ReferenceError, str, p)
@unittest.skipUnless(hasattr(set, "test_c_api"),
'C API test only available in a debug build')
......
......@@ -22,6 +22,7 @@
#include "runtime/ics.h"
#include "runtime/inline/list.h"
#include "runtime/objmodel.h"
#include "runtime/set.h"
#include "runtime/types.h"
#include "runtime/util.h"
......@@ -510,11 +511,12 @@ Box* dictSetdefault(BoxedDict* self, Box* k, Box* v) {
raiseExcHelper(TypeError, "descriptor 'setdefault' requires a 'dict' object but received a '%s'",
getTypeName(self));
auto it = self->d.find(k);
BoxAndHash k_hash(k);
auto it = self->d.find(k_hash);
if (it != self->d.end())
return it->second;
self->d.insert(std::make_pair(k, v));
self->d.insert(std::make_pair(k_hash, v));
return v;
}
......@@ -559,9 +561,15 @@ Box* dictNonzero(BoxedDict* self) {
Box* dictFromkeys(Box* cls, Box* iterable, Box* default_value) {
auto rtn = new BoxedDict();
if (PyAnySet_Check(iterable)) {
for (auto&& elt : ((BoxedSet*)iterable)->s) {
rtn->d.insert(std::make_pair(elt, default_value));
}
} else {
for (Box* e : iterable->pyElements()) {
dictSetitem(rtn, e, default_value);
}
}
return rtn;
}
......
......@@ -39,8 +39,9 @@ class BoxedSetIterator : public Box {
public:
BoxedSet* s;
decltype(BoxedSet::s)::iterator it;
long size;
BoxedSetIterator(BoxedSet* s) : s(s), it(s->s.begin()) {}
BoxedSetIterator(BoxedSet* s) : s(s), it(s->s.begin()), size(s->s.size()) {}
DEFAULT_CLASS(set_iterator_cls);
......@@ -68,9 +69,17 @@ Box* setiteratorHasnext(BoxedSetIterator* self) {
Box* setiteratorNext(BoxedSetIterator* self) {
RELEASE_ASSERT(self->cls == set_iterator_cls, "");
if (self->s->s.size() != self->size) {
raiseExcHelper(RuntimeError, "Set changed size during iteration");
}
return self->next();
}
Box* setiteratorLength(BoxedSetIterator* self) {
RELEASE_ASSERT(self->cls == set_iterator_cls, "");
return boxInt(self->s->s.size());
}
Box* setiter_next(Box* _self) noexcept {
RELEASE_ASSERT(_self->cls == set_iterator_cls, "");
BoxedSetIterator* self = (BoxedSetIterator*)_self;
......@@ -100,51 +109,90 @@ BoxedSet* makeNewSet(BoxedClass* cls, Box* container) {
BoxedSet* rtn = new (cls) BoxedSet();
if (container) {
for (Box* e : container->pyElements()) {
rtn->s.insert(e);
if (PyAnySet_Check(container)) {
for (auto&& elt : ((BoxedSet*)container)->s) {
rtn->s.insert(elt);
}
} else if (PyDict_CheckExact(container)) {
for (auto&& elt : ((BoxedDict*)container)->d) {
rtn->s.insert(elt.first);
}
} else {
for (auto elt : container->pyElements()) {
rtn->s.insert(elt);
}
}
}
return rtn;
}
Box* frozensetNew(Box* _cls, Box* container) {
Box* frozensetNew(Box* _cls, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(_cls->cls == type_cls, "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, frozenset_cls), "");
if (_cls == frozenset_cls && !_PyArg_NoKeywords("frozenset()", kwargs)) {
throwCAPIException();
}
// Some optimizations from CPython: frozensets can be shared:
if (_cls == frozenset_cls) {
if (!container) {
static Box* emptyfrozenset = PyGC_AddRoot(new (frozenset_cls) BoxedSet());
return emptyfrozenset;
if (_cls != frozenset_cls) {
return makeNewSet(cls, container);
}
if (container != NULL) {
if (container->cls == frozenset_cls)
return container;
}
return makeNewSet(cls, container);
BoxedSet* result = makeNewSet(cls, container);
if (result->s.size() != 0) {
return result;
}
}
static Box* emptyfrozenset = PyGC_AddRoot(new (frozenset_cls) BoxedSet());
return emptyfrozenset;
}
Box* setNew(Box* _cls, Box* container) {
Box* setNew(Box* _cls, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(_cls->cls == type_cls, "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, set_cls), "");
if (_cls == set_cls && !_PyArg_NoKeywords("set()", kwargs)) {
throwCAPIException();
}
// Note: set.__new__ explicitly ignores the container argument.
return makeNewSet(cls, NULL);
}
Box* setInit(Box* _self, Box* container) {
Box* setInit(Box* _self, Box* container, BoxedDict* kwargs) {
RELEASE_ASSERT(PySet_Check(_self), "");
if (PySet_Check(_self) && !_PyArg_NoKeywords("set()", kwargs)) {
throwCAPIException();
}
if (!container)
return None;
BoxedSet* self = static_cast<BoxedSet*>(_self);
for (Box* e : container->pyElements()) {
self->s.insert(e);
self->s.clear();
if (PyAnySet_Check(container)) {
for (auto&& elt : ((BoxedSet*)container)->s) {
self->s.insert(elt);
}
} else if (PyDict_CheckExact(container)) {
for (auto&& elt : ((BoxedDict*)container)->d) {
self->s.insert(elt.first);
}
} else {
for (auto elt : container->pyElements()) {
self->s.insert(elt);
}
}
return None;
}
......@@ -203,6 +251,7 @@ static void _setSymmetricDifferenceUpdate(BoxedSet* self, Box* other) {
other = makeNewSet(self->cls, other);
BoxedSet* other_set = static_cast<BoxedSet*>(other);
for (auto elt : other_set->s) {
bool found = self->s.erase(elt);
if (!found)
......@@ -213,7 +262,7 @@ static void _setSymmetricDifferenceUpdate(BoxedSet* self, Box* other) {
static BoxedSet* setIntersection2(BoxedSet* self, Box* container) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
BoxedSet* rtn = new BoxedSet();
BoxedSet* rtn = makeNewSet(self->cls, NULL);
for (auto elt : container->pyElements()) {
if (self->s.count(elt))
rtn->s.insert(elt);
......@@ -340,27 +389,6 @@ extern "C" int PySet_Add(PyObject* set, PyObject* key) noexcept {
}
}
Box* setRemove(BoxedSet* self, Box* v) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
auto it = self->s.find(v);
if (it == self->s.end()) {
raiseExcHelper(KeyError, v);
}
self->s.erase(it);
return None;
}
Box* setDiscard(BoxedSet* self, Box* v) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
auto it = self->s.find(v);
if (it != self->s.end())
self->s.erase(it);
return None;
}
Box* setClear(BoxedSet* self, Box* v) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
......@@ -401,7 +429,7 @@ Box* setUnion(BoxedSet* self, BoxedTuple* args) {
if (!PyAnySet_Check(self))
raiseExcHelper(TypeError, "descriptor 'union' requires a 'set' object but received a '%s'", getTypeName(self));
BoxedSet* rtn = new BoxedSet();
BoxedSet* rtn = makeNewSet(self->cls, self);
rtn->s.insert(self->s.begin(), self->s.end());
for (auto container : args->pyElements()) {
......@@ -414,11 +442,21 @@ Box* setUnion(BoxedSet* self, BoxedTuple* args) {
}
static void _setDifferenceUpdate(BoxedSet* self, BoxedTuple* args) {
for (auto container : *args) {
for (auto container : args->pyElements()) {
if (PyAnySet_Check(container)) {
for (auto&& elt : ((BoxedSet*)container)->s) {
self->s.erase(elt);
}
} else if (PyDict_CheckExact(container)) {
for (auto&& elt : ((BoxedDict*)container)->d) {
self->s.erase(elt.first);
}
} else {
for (auto elt : container->pyElements()) {
self->s.erase(elt);
}
}
}
}
Box* setDifferenceUpdate(BoxedSet* self, BoxedTuple* args) {
......@@ -528,6 +566,15 @@ Box* setCopy(BoxedSet* self) {
return rtn;
}
Box* frozensetCopy(BoxedSet* self) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
if (self->cls == frozenset_cls) {
return self;
}
return setCopy(self);
}
Box* setPop(BoxedSet* self) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
......@@ -540,11 +587,6 @@ Box* setPop(BoxedSet* self) {
return rtn;
}
Box* setContains(BoxedSet* self, Box* v) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
return boxBool(self->s.find(v) != self->s.end());
}
Box* setEq(BoxedSet* self, BoxedSet* rhs) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
if (!PyAnySet_Check(rhs))
......@@ -600,20 +642,116 @@ Box* setGt(BoxedSet* self, BoxedSet* rhs) {
return setIssuperset(self, rhs);
}
Box* setContains(BoxedSet* self, Box* key) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
if (PySet_Check(key)) {
try {
BoxAndHash k_hash(key);
return boxBool(self->s.find(k_hash) != self->s.end());
} catch (ExcInfo e) {
if (!e.matches(TypeError))
throw e;
BoxedSet* tmpKey = makeNewSet(frozenset_cls, key);
return boxBool(self->s.find(tmpKey) != self->s.end());
}
}
return boxBool(self->s.find(key) != self->s.end());
}
Box* setRemove(BoxedSet* self, Box* key) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
if (PySet_Check(key)) {
try {
BoxAndHash k_hash(key);
if (self->s.find(k_hash) != self->s.end()) {
bool existed = self->s.erase(k_hash);
if (existed)
return None;
}
} catch (ExcInfo e) {
if (!e.matches(TypeError))
throw e;
BoxedSet* tmpKey = makeNewSet(frozenset_cls, key);
if (self->s.find(tmpKey) != self->s.end()) {
bool existed = self->s.erase(tmpKey);
if (existed)
return None;
}
}
raiseExcHelper(KeyError, key);
}
auto it = self->s.find(key);
if (it == self->s.end()) {
raiseExcHelper(KeyError, key);
}
self->s.erase(it);
return None;
}
Box* setDiscard(BoxedSet* self, Box* key) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
if (PySet_Check(key)) {
try {
BoxAndHash k_hash(key);
if (self->s.find(k_hash) != self->s.end()) {
self->s.erase(k_hash);
}
} catch (ExcInfo e) {
if (!e.matches(TypeError))
throw e;
BoxedSet* tmpKey = makeNewSet(frozenset_cls, key);
if (self->s.find(tmpKey) != self->s.end()) {
self->s.erase(tmpKey);
}
}
return None;
}
auto it = self->s.find(key);
if (it != self->s.end())
self->s.erase(it);
return None;
}
Box* setNocmp(BoxedSet* self, BoxedSet* rhs) {
raiseExcHelper(TypeError, "cannot compare sets using cmp()");
}
Box* setNonzero(BoxedSet* self) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
return boxBool(self->s.size());
}
Box* setNotImplemented(BoxedSet* self) {
raiseExcHelper(TypeError, "unhashable type: 'set'");
}
Box* setHash(BoxedSet* self) {
RELEASE_ASSERT(isSubclass(self->cls, frozenset_cls), "");
int64_t rtn = 1927868237L;
int64_t h, hash = 1927868237L;
hash *= self->s.size() + 1;
for (auto&& e : self->s) {
rtn ^= e.hash + 0x9e3779b9 + (rtn << 6) + (rtn >> 2);
h = e.hash;
hash ^= (h ^ (h << 16) ^ 89869747L) * 3644798167u;
}
return boxInt(rtn);
hash = hash * 69069L + 907133923L;
if (hash == -1)
hash = 590923713L;
return boxInt(hash);
}
extern "C" PyObject* PySet_New(PyObject* iterable) noexcept {
......@@ -698,14 +836,16 @@ void setupSet() {
set_iterator_cls->giveAttr("__hasnext__",
new BoxedFunction(boxRTFunction((void*)setiteratorHasnext, BOXED_BOOL, 1)));
set_iterator_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)setiteratorNext, UNKNOWN, 1)));
set_iterator_cls->giveAttr("__length_hint__",
new BoxedFunction(boxRTFunction((void*)setiteratorLength, BOXED_INT, 1)));
set_iterator_cls->freeze();
set_iterator_cls->tp_iternext = setiter_next;
set_iterator_cls->tp_iter = PyObject_SelfIter;
set_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)setNew, UNKNOWN, 2, false, false), { NULL }));
set_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)setInit, UNKNOWN, 2, false, false), { NULL }));
set_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)setNew, UNKNOWN, 2, false, true), { NULL }));
set_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)setInit, UNKNOWN, 2, false, true), { NULL }));
frozenset_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)frozensetNew, UNKNOWN, 2, false, false), { NULL }));
new BoxedFunction(boxRTFunction((void*)frozensetNew, UNKNOWN, 2, false, true), { NULL }));
Box* set_repr = new BoxedFunction(boxRTFunction((void*)setRepr, STR, 1));
set_cls->giveAttr("__repr__", set_repr);
......@@ -762,6 +902,8 @@ void setupSet() {
set_cls->giveAttr("__contains__", new BoxedFunction(boxRTFunction((void*)setContains, BOXED_BOOL, 2)));
frozenset_cls->giveAttr("__contains__", set_cls->getattr(internStringMortal("__contains__")));
set_cls->giveAttr("__cmp__", new BoxedFunction(boxRTFunction((void*)setNocmp, NONE, 2)));
frozenset_cls->giveAttr("__cmp__", new BoxedFunction(boxRTFunction((void*)setNocmp, NONE, 2)));
set_cls->giveAttr("__eq__", new BoxedFunction(boxRTFunction((void*)setEq, BOXED_BOOL, 2)));
frozenset_cls->giveAttr("__eq__", set_cls->getattr(internStringMortal("__eq__")));
set_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)setNe, BOXED_BOOL, 2)));
......@@ -811,6 +953,7 @@ void setupSet() {
frozenset_cls->giveAttr("isdisjoint", set_cls->getattr(internStringMortal("isdisjoint")));
set_cls->giveAttr("copy", new BoxedFunction(boxRTFunction((void*)setCopy, UNKNOWN, 1)));
frozenset_cls->giveAttr("copy", new BoxedFunction(boxRTFunction((void*)frozensetCopy, UNKNOWN, 1)));
set_cls->giveAttr("pop", new BoxedFunction(boxRTFunction((void*)setPop, UNKNOWN, 1)));
for (auto& md : set_methods) {
......
......@@ -199,3 +199,54 @@ try:
set(**dict(a=1))
except TypeError:
print "TypeError"
class MySet(set):
def __new__(cls, *args, **kwargs):
return set.__new__(cls, *args)
try:
MySet(a=1)
except TypeError as e:
print(e.message)
class SetSubclassWithKeywordArgs(set):
def __init__(self, iterable=[], newarg=None):
set.__init__(self, iterable)
SetSubclassWithKeywordArgs(newarg=1)
try:
frozenset(a=1)
except TypeError as e:
print(e.message)
class MyFrozenSet(frozenset):
def __new__(cls, *args, **kwargs):
return frozenset.__new__(cls, *args)
MyFrozenSet(a=1)
class FrozensetSubclassWithKeywordArgs(frozenset):
def __init__(self, iterable=[], newarg=None):
frozenset.__init__(self, iterable)
FrozensetSubclassWithKeywordArgs(newarg=1)
print(set() in frozenset([frozenset()]))
class MySet(set):
def __hash__(self):
print("calling __hash__")
return id(self)
print("Ready")
foo = MySet()
a = set()
a.add(foo)
print(a.remove(foo))
print(foo in set())
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