Commit cf9a690c authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #894 from kmod/sqlalchemy

Sqlalchemy compatibility fixes
parents 2442b5f3 5ae9cb6b
...@@ -92,24 +92,64 @@ Box* setAdd2(Box* _self, Box* b) { ...@@ -92,24 +92,64 @@ Box* setAdd2(Box* _self, Box* b) {
return None; return None;
} }
// Creates a set of type 'cls' from 'container' (NULL to get an empty set).
// Works for frozenset and normal set types.
BoxedSet* makeNewSet(BoxedClass* cls, Box* container) {
assert(isSubclass(cls, frozenset_cls) || isSubclass(cls, set_cls));
BoxedSet* rtn = new (cls) BoxedSet();
if (container) {
for (Box* e : container->pyElements()) {
rtn->s.insert(e);
}
}
return rtn;
}
Box* frozensetNew(Box* _cls, Box* container) {
RELEASE_ASSERT(_cls->cls == type_cls, "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, frozenset_cls), "");
// 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 (container->cls == frozenset_cls)
return container;
}
return makeNewSet(cls, container);
}
Box* setNew(Box* _cls, Box* container) { Box* setNew(Box* _cls, Box* container) {
RELEASE_ASSERT(_cls->cls == type_cls, ""); RELEASE_ASSERT(_cls->cls == type_cls, "");
BoxedClass* cls = static_cast<BoxedClass*>(_cls); BoxedClass* cls = static_cast<BoxedClass*>(_cls);
RELEASE_ASSERT(isSubclass(cls, set_cls) || isSubclass(cls, frozenset_cls), ""); RELEASE_ASSERT(isSubclass(cls, set_cls), "");
BoxedSet* rtn = new (cls) BoxedSet(); // Note: set.__new__ explicitly ignores the container argument.
return makeNewSet(cls, NULL);
}
if (container == None) Box* setInit(Box* _self, Box* container) {
return rtn; RELEASE_ASSERT(PySet_Check(_self), "");
if (!container)
return None;
BoxedSet* self = static_cast<BoxedSet*>(_self);
for (Box* e : container->pyElements()) { for (Box* e : container->pyElements()) {
rtn->s.insert(e); self->s.insert(e);
} }
return None;
return rtn;
} }
static Box* _setRepr(BoxedSet* self, const char* type_name) { static Box* setRepr(BoxedSet* self) {
RELEASE_ASSERT(PyAnySet_Check(self), "");
std::vector<char> chars; std::vector<char> chars;
int status = Py_ReprEnter((PyObject*)self); int status = Py_ReprEnter((PyObject*)self);
...@@ -118,7 +158,7 @@ static Box* _setRepr(BoxedSet* self, const char* type_name) { ...@@ -118,7 +158,7 @@ static Box* _setRepr(BoxedSet* self, const char* type_name) {
if (status < 0) if (status < 0)
throwCAPIException(); throwCAPIException();
std::string ty = std::string(type_name); std::string ty = std::string(self->cls->tp_name);
chars.insert(chars.end(), ty.begin(), ty.end()); chars.insert(chars.end(), ty.begin(), ty.end());
chars.push_back('('); chars.push_back('(');
chars.push_back('.'); chars.push_back('.');
...@@ -130,7 +170,7 @@ static Box* _setRepr(BoxedSet* self, const char* type_name) { ...@@ -130,7 +170,7 @@ static Box* _setRepr(BoxedSet* self, const char* type_name) {
} }
try { try {
std::string ty = std::string(type_name); std::string ty = std::string(self->cls->tp_name);
chars.insert(chars.end(), ty.begin(), ty.end()); chars.insert(chars.end(), ty.begin(), ty.end());
chars.push_back('('); chars.push_back('(');
...@@ -158,16 +198,6 @@ static Box* _setRepr(BoxedSet* self, const char* type_name) { ...@@ -158,16 +198,6 @@ static Box* _setRepr(BoxedSet* self, const char* type_name) {
return boxString(llvm::StringRef(&chars[0], chars.size())); return boxString(llvm::StringRef(&chars[0], chars.size()));
} }
Box* setRepr(BoxedSet* self) {
RELEASE_ASSERT(isSubclass(self->cls, set_cls), "");
return _setRepr(self, "set");
}
Box* frozensetRepr(BoxedSet* self) {
RELEASE_ASSERT(isSubclass(self->cls, frozenset_cls), "");
return _setRepr(self, "frozenset");
}
Box* setOrSet(BoxedSet* lhs, BoxedSet* rhs) { Box* setOrSet(BoxedSet* lhs, BoxedSet* rhs) {
RELEASE_ASSERT(PyAnySet_Check(lhs), ""); RELEASE_ASSERT(PyAnySet_Check(lhs), "");
RELEASE_ASSERT(PyAnySet_Check(rhs), ""); RELEASE_ASSERT(PyAnySet_Check(rhs), "");
...@@ -343,7 +373,7 @@ Box* setDifference(BoxedSet* self, BoxedTuple* args) { ...@@ -343,7 +373,7 @@ Box* setDifference(BoxedSet* self, BoxedTuple* args) {
raiseExcHelper(TypeError, "descriptor 'difference' requires a 'set' object but received a '%s'", raiseExcHelper(TypeError, "descriptor 'difference' requires a 'set' object but received a '%s'",
getTypeName(self)); getTypeName(self));
BoxedSet* rtn = (BoxedSet*)setNew(self->cls, self); BoxedSet* rtn = makeNewSet(self->cls, self);
for (auto container : args->pyElements()) { for (auto container : args->pyElements()) {
for (auto elt : container->pyElements()) { for (auto elt : container->pyElements()) {
...@@ -372,7 +402,7 @@ static Box* setIssubset(BoxedSet* self, Box* container) { ...@@ -372,7 +402,7 @@ static Box* setIssubset(BoxedSet* self, Box* container) {
RELEASE_ASSERT(PyAnySet_Check(self), ""); RELEASE_ASSERT(PyAnySet_Check(self), "");
if (!PyAnySet_Check(container)) { if (!PyAnySet_Check(container)) {
container = setNew(set_cls, container); container = makeNewSet(set_cls, container);
} }
assert(PyAnySet_Check(container)); assert(PyAnySet_Check(container));
...@@ -388,7 +418,7 @@ static Box* setIssuperset(BoxedSet* self, Box* container) { ...@@ -388,7 +418,7 @@ static Box* setIssuperset(BoxedSet* self, Box* container) {
RELEASE_ASSERT(PyAnySet_Check(self), ""); RELEASE_ASSERT(PyAnySet_Check(self), "");
if (!PyAnySet_Check(container)) { if (!PyAnySet_Check(container)) {
container = setNew(set_cls, container); container = makeNewSet(set_cls, container);
} }
assert(PyAnySet_Check(container)); assert(PyAnySet_Check(container));
...@@ -548,16 +578,17 @@ void setupSet() { ...@@ -548,16 +578,17 @@ void setupSet() {
set_iterator_cls->tp_iternext = setiter_next; set_iterator_cls->tp_iternext = setiter_next;
set_cls->giveAttr("__new__", set_cls->giveAttr("__new__",
new BoxedFunction(boxRTFunction((void*)setNew, UNKNOWN, 2, 1, false, false), { None })); new BoxedFunction(boxRTFunction((void*)setNew, UNKNOWN, 2, 1, false, false), { NULL }));
frozenset_cls->giveAttr("__new__", set_cls->getattr(internStringMortal("__new__"))); set_cls->giveAttr("__init__",
new BoxedFunction(boxRTFunction((void*)setInit, UNKNOWN, 2, 1, false, false), { NULL }));
frozenset_cls->giveAttr(
"__new__", new BoxedFunction(boxRTFunction((void*)frozensetNew, UNKNOWN, 2, 1, false, false), { NULL }));
Box* set_repr = new BoxedFunction(boxRTFunction((void*)setRepr, STR, 1)); Box* set_repr = new BoxedFunction(boxRTFunction((void*)setRepr, STR, 1));
set_cls->giveAttr("__repr__", set_repr); set_cls->giveAttr("__repr__", set_repr);
set_cls->giveAttr("__str__", set_repr); set_cls->giveAttr("__str__", set_repr);
frozenset_cls->giveAttr("__repr__", set_repr);
Box* frozenset_repr = new BoxedFunction(boxRTFunction((void*)frozensetRepr, STR, 1)); frozenset_cls->giveAttr("__str__", set_repr);
frozenset_cls->giveAttr("__repr__", frozenset_repr);
frozenset_cls->giveAttr("__str__", frozenset_repr);
std::vector<ConcreteCompilerType*> v_ss, v_sf, v_su, v_ff, v_fs, v_fu; std::vector<ConcreteCompilerType*> v_ss, v_sf, v_su, v_ff, v_fs, v_fu;
v_ss.push_back(SET); v_ss.push_back(SET);
......
...@@ -346,10 +346,10 @@ extern "C" Box* strAdd(BoxedString* lhs, Box* _rhs) { ...@@ -346,10 +346,10 @@ extern "C" Box* strAdd(BoxedString* lhs, Box* _rhs) {
checkAndThrowCAPIException(); checkAndThrowCAPIException();
return rtn; return rtn;
} else { } else {
// Note: this is deliberately not returning NotImplemented, even though // This is a compatibility break with CPython, which has their sq_concat method
// that would be more usual. I assume this behavior of CPython's is // directly throw a TypeError. Since we're not implementing this as a sq_concat,
// for backwards compatibility. // return NotImplemented for now.
raiseExcHelper(TypeError, "cannot concatenate 'str' and '%s' objects", getTypeName(_rhs)); return NotImplemented;
} }
} }
......
...@@ -1602,6 +1602,8 @@ extern "C" BoxedString* builtinFunctionOrMethodRepr(BoxedBuiltinFunctionOrMethod ...@@ -1602,6 +1602,8 @@ extern "C" BoxedString* builtinFunctionOrMethodRepr(BoxedBuiltinFunctionOrMethod
} }
extern "C" BoxedString* functionRepr(BoxedFunction* v) { extern "C" BoxedString* functionRepr(BoxedFunction* v) {
if (!v->name)
return (BoxedString*)PyString_FromFormat("<function <name_missing?> at %p>", v);
return (BoxedString*)PyString_FromFormat("<function %s at %p>", PyString_AsString(v->name), v); return (BoxedString*)PyString_FromFormat("<function %s at %p>", PyString_AsString(v->name), v);
} }
......
...@@ -44,7 +44,12 @@ import sqlalchemy.testing ...@@ -44,7 +44,12 @@ import sqlalchemy.testing
class Requirements(object): class Requirements(object):
def __getattr__(self, n): def __getattr__(self, n):
def inner(f): def inner(f):
return f if n == "predictable_gc":
def f2(*args, **kw):
return
return f2
else:
return f
inner.not_ = lambda: inner inner.not_ = lambda: inner
return inner return inner
sqlalchemy.testing.config.requirements = sqlalchemy.testing.requires = Requirements() sqlalchemy.testing.config.requirements = sqlalchemy.testing.requires = Requirements()
...@@ -56,11 +61,6 @@ test_files = glob.glob(TEST_DIR + "/test*.py") + glob.glob(TEST_DIR + "/*/test*. ...@@ -56,11 +61,6 @@ test_files = glob.glob(TEST_DIR + "/test*.py") + glob.glob(TEST_DIR + "/*/test*.
# infrastructure to run): # infrastructure to run):
MODULES_TO_TEST = ['test.engine.test_parseconnect', 'test.ext.test_compiler', 'test.dialect.test_pyodbc', 'test.dialect.test_sybase', 'test.dialect.test_mxodbc', 'test.sql.test_inspect', 'test.sql.test_operators', 'test.sql.test_ddlemit', 'test.sql.test_cte', 'test.base.test_dependency', 'test.base.test_except', 'test.base.test_inspect', 'test.base.test_events', 'test.orm.test_inspect', 'test.orm.test_descriptor'] MODULES_TO_TEST = ['test.engine.test_parseconnect', 'test.ext.test_compiler', 'test.dialect.test_pyodbc', 'test.dialect.test_sybase', 'test.dialect.test_mxodbc', 'test.sql.test_inspect', 'test.sql.test_operators', 'test.sql.test_ddlemit', 'test.sql.test_cte', 'test.base.test_dependency', 'test.base.test_except', 'test.base.test_inspect', 'test.base.test_events', 'test.orm.test_inspect', 'test.orm.test_descriptor']
# These are currently broken on Pyston:
MODULES_TO_TEST.remove("test.sql.test_operators")
MODULES_TO_TEST.remove("test.base.test_events")
MODULES_TO_TEST.remove("test.orm.test_descriptor")
passed = [] passed = []
failed = [] failed = []
...@@ -85,6 +85,9 @@ for fn in test_files: ...@@ -85,6 +85,9 @@ for fn in test_files:
for t in dir(n): for t in dir(n):
if not t.startswith("test_"): if not t.startswith("test_"):
continue continue
if nname == "SubclassGrowthTest" and t == "test_subclass":
# This test should be marked as requiring predictable_pc
continue
print "Running", t print "Running", t
n.setup() n.setup()
getattr(n, t)() getattr(n, t)()
......
...@@ -10,7 +10,7 @@ def f(): ...@@ -10,7 +10,7 @@ def f():
i += IntLike() i += IntLike()
print i print i
try: try:
i + 1 1 + i
except TypeError, e: except TypeError, e:
print e print e
f() f()
...@@ -139,3 +139,33 @@ s2 = set([1, 5]) ...@@ -139,3 +139,33 @@ s2 = set([1, 5])
s1.intersection_update(s2) s1.intersection_update(s2)
print sorted(s1) print sorted(s1)
def test_set_creation(base):
print "Testing with base =", base
# set.__new__ should not iterate through the argument.
# sqlalchemy overrides init and expects to be able to do the iteration there.
def g():
for i in xrange(5):
print "iterating", i
yield i
print "Calling __new__:"
s = base.__new__(base, g())
print "Calling __init__:"
s.__init__(g())
print "Trying subclassing"
class MySet(base):
def __new__(cls, g):
print "starting new"
r = base.__new__(cls, g)
print "ending new"
return r
def __init__(self, g):
print "starting init"
print list(g)
print MySet(g())
test_set_creation(set)
test_set_creation(frozenset)
# expected: fail
# str has some weird adding behavior that cannot be replicated with Python code. # str has some weird adding behavior that cannot be replicated with Python code.
# Usually, if an __add__ throws an error, then __radd__ isn't tried. # Usually, if an __add__ throws an error, then __radd__ isn't tried.
# But string addition is defined in sq_concat, which is tried after __add__ and # But string addition is defined in sq_concat, which is tried after __add__ and
...@@ -13,10 +11,8 @@ class D(object): ...@@ -13,10 +11,8 @@ class D(object):
def __radd__(self, lhs): def __radd__(self, lhs):
return 3.14 return 3.14
try: print "" + D()
print "".__add__(D())
except TypeError as e:
print e
try: try:
print C() + D() print C() + D()
except TypeError as e: except TypeError as e:
......
# expected: fail
# CPython's str.__add__ directly throws a TypeError.
# Ours (and PyPy's) returns NotImplemented.
try:
print "".__add__(1)
except TypeError as e:
print e
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