Commit 3282f100 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Test + start supporting the opposite direction (CAPI->Python) as well

parent 8b3f6190
......@@ -54,8 +54,6 @@ static PyObject* wrap_unaryfunc(PyObject* self, PyObject* args, void* wrapped) {
PyObject* Py_CallPythonNew(PyTypeObject* self, PyObject* args, PyObject* kwds) {
try {
Py_FatalError("this function is untested");
// TODO: runtime ICs?
Box* new_attr = typeLookup(self, _new_str, NULL);
assert(new_attr);
......@@ -80,7 +78,7 @@ PyObject* Py_CallPythonCall(PyObject* self, PyObject* args, PyObject* kwds) {
PyObject* Py_CallPythonRepr(PyObject* self) {
try {
Py_FatalError("unimplemented");
return repr(self);
} catch (Box* e) {
abort();
}
......@@ -145,6 +143,15 @@ void fixup_slot_dispatchers(BoxedClass* self) {
for (const slotdef& p : slotdefs) {
update_one_slot(self, p);
}
// TODO: CPython handles this by having the __name__ attribute wrap (via a getset object)
// the tp_name field, whereas we're (needlessly?) doing the opposite.
if (!self->tp_name) {
Box* b = self->getattr("__name__");
assert(b);
assert(b->cls == str_cls);
self->tp_name = static_cast<BoxedString*>(b)->s.c_str();
}
}
static PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) {
......
......@@ -254,8 +254,12 @@ extern "C" PyObject* PyObject_GetIter(PyObject*) {
Py_FatalError("unimplemented");
}
extern "C" PyObject* PyObject_Repr(PyObject*) {
Py_FatalError("unimplemented");
extern "C" PyObject* PyObject_Repr(PyObject* obj) {
try {
return repr(obj);
} catch (Box* b) {
Py_FatalError("unimplemented");
}
}
extern "C" PyObject* PyObject_GetAttr(PyObject* o, PyObject* attr_name) {
......@@ -332,6 +336,10 @@ extern "C" int PyObject_GetBuffer(PyObject* exporter, Py_buffer* view, int flags
Py_FatalError("unimplemented");
}
extern "C" int PyObject_Print(PyObject* obj, FILE* fp, int flags) {
Py_FatalError("unimplemented");
};
extern "C" int PySequence_Check(PyObject*) {
Py_FatalError("unimplemented");
}
......
......@@ -90,7 +90,43 @@ static PyTypeObject slots_tester = {
0, /* tp_free */
};
// Tests the correctness of the CAPI slots when the attributes get set in Python code:
static PyObject *
call_funcs(PyObject* _module, PyObject* args) {
PyObject* obj;
if (!PyArg_ParseTuple(args, "O", &obj))
return NULL;
printf("\n");
PyTypeObject* cls = Py_TYPE(obj);
printf("Received a %s object\n", cls->tp_name);
if (cls->tp_repr) {
PyObject* rtn = cls->tp_repr(obj);
printf("tp_repr exists and returned: '%s'\n", PyString_AsString(rtn));
Py_DECREF(rtn);
}
if (cls->tp_new) {
PyObject* rtn = cls->tp_new(cls, PyTuple_New(0), PyDict_New());
printf("tp_new exists and returned an object of type: '%s'\n", Py_TYPE(rtn)->tp_name);
Py_DECREF(rtn);
}
if (cls->tp_call) {
printf("tp_call exists\n");
} else {
printf("tp_call doesnt exist\n");
}
Py_DECREF(obj);
Py_RETURN_NONE;
}
static PyMethodDef SlotsMethods[] = {
{"call_funcs", call_funcs, METH_VARARGS, "Call slotted functions."},
{NULL, NULL, 0, NULL} /* Sentinel */
};
......
......@@ -5,3 +5,16 @@ for i in xrange(3):
print t, repr(t), t()
print slots_test.SlotsTester.set_through_tpdict, slots_test.SlotsTester(5).set_through_tpdict
class C(object):
def __repr__(self):
print "__repr__()"
return "<C object>"
slots_test.call_funcs(C())
# Test to make sure that updating an existing class also updates the tp_* slots:
def repr2(self):
return "repr2()"
C.repr2 = repr2
slots_test.call_funcs(C())
......@@ -52,6 +52,21 @@ def set_ulimits():
MAX_MEM_MB = 100
resource.setrlimit(resource.RLIMIT_RSS, (MAX_MEM_MB * 1024 * 1024, MAX_MEM_MB * 1024 * 1024))
EXTMODULE_DIR = os.path.dirname(os.path.realpath(__file__)) + "/../test/test_extension/build/lib.linux-x86_64-2.7/"
_extmodule_mtime = None
def get_extmodule_mtime():
global _extmodule_mtime
if _extmodule_mtime is not None:
return _extmodule_mtime
rtn = 0
for fn in os.listdir(EXTMODULE_DIR):
if not fn.endswith(".so"):
continue
rtn = max(rtn, os.stat(os.path.join(EXTMODULE_DIR, fn)).st_mtime)
_extmodule_mtime = rtn
return rtn
def get_expected_output(fn):
sys.stdout.flush()
assert fn.endswith(".py")
......@@ -61,7 +76,8 @@ def get_expected_output(fn):
cache_fn = fn[:-3] + ".expected_cache"
if os.path.exists(cache_fn):
if os.stat(cache_fn).st_mtime > os.stat(fn).st_mtime:
cache_mtime = os.stat(cache_fn).st_mtime
if cache_mtime > os.stat(fn).st_mtime and cache_mtime > get_extmodule_mtime():
try:
return cPickle.load(open(cache_fn))
except EOFError:
......@@ -69,7 +85,7 @@ def get_expected_output(fn):
# TODO don't suppress warnings globally:
env = dict(os.environ)
env["PYTHONPATH"] = os.path.dirname(os.path.realpath(__file__)) + "/../test/test_extension/build/lib.linux-x86_64-2.7/"
env["PYTHONPATH"] = EXTMODULE_DIR
p = subprocess.Popen(["python", "-Wignore", fn], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=open("/dev/null"), preexec_fn=set_ulimits, env=env)
out, err = p.communicate()
code = p.wait()
......@@ -208,7 +224,7 @@ def run_test(fn, check_stats, run_memcheck):
out_fd, out_fn = tempfile.mkstemp(prefix="received_")
os.fdopen(exp_fd, 'w').write(expected_out)
os.fdopen(out_fd, 'w').write(out)
p = subprocess.Popen(["diff", "-C2", "-a", exp_fn, out_fn], stdout=subprocess.PIPE, preexec_fn=set_ulimits)
p = subprocess.Popen(["diff", "-u", "-a", exp_fn, out_fn], stdout=subprocess.PIPE, preexec_fn=set_ulimits)
diff = p.stdout.read()
assert p.wait() in (0, 1)
os.unlink(exp_fn)
......
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