clean up the implementation of PythonCallable::GetNumArguments

Summary:
The current implementation of PythonCallable::GetNumArguments
is not exception safe, has weird semantics, and is just plain
incorrect for some kinds of functions.

Python 3.3 introduces inspect.signature, which lets us easily
query for function signatures in a sane and documented way.

This patch leaves the old implementation in place for < 3.3,
but uses inspect.signature for modern pythons.   It also leaves
the old weird semantics in place, but with FIXMEs grousing about
it.   We should update the callers and fix the semantics in a
subsequent patch.    It also adds some tests.

Reviewers: JDevlieghere, clayborg, labath, jingham

Reviewed By: labath

Subscribers: lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D68995

llvm-svn: 375181
This commit is contained in:
Lawrence D'Anna
2019-10-17 22:22:06 +00:00
parent 3d737b642a
commit c86a6acaee
3 changed files with 329 additions and 35 deletions

View File

@@ -20,6 +20,7 @@
#include "PythonTestSuite.h"
using namespace lldb_private;
using namespace lldb_private::python;
class PythonDataObjectsTest : public PythonTestSuite {
public:
@@ -626,3 +627,116 @@ TEST_F(PythonDataObjectsTest, TestExtractingUInt64ThroughStructuredData) {
}
}
}
TEST_F(PythonDataObjectsTest, TestCallable) {
PythonDictionary globals(PyInitialValue::Empty);
auto builtins = PythonModule::BuiltinsModule();
llvm::Error error = globals.SetItem("__builtins__", builtins);
ASSERT_FALSE(error);
{
PyObject *o = PyRun_String("lambda x : x", Py_eval_input, globals.get(),
globals.get());
ASSERT_FALSE(o == NULL);
auto lambda = Take<PythonCallable>(o);
auto arginfo = lambda.GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 1);
EXPECT_EQ(arginfo.get().has_varargs, false);
EXPECT_EQ(arginfo.get().is_bound_method, false);
}
{
PyObject *o = PyRun_String("lambda x,y=0: x", Py_eval_input, globals.get(),
globals.get());
ASSERT_FALSE(o == NULL);
auto lambda = Take<PythonCallable>(o);
auto arginfo = lambda.GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 2);
EXPECT_EQ(arginfo.get().has_varargs, false);
EXPECT_EQ(arginfo.get().is_bound_method, false);
}
{
PyObject *o = PyRun_String("lambda x,y=0, **kw: x", Py_eval_input,
globals.get(), globals.get());
ASSERT_FALSE(o == NULL);
auto lambda = Take<PythonCallable>(o);
auto arginfo = lambda.GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 2);
EXPECT_EQ(arginfo.get().has_varargs, false);
}
{
PyObject *o = PyRun_String("lambda x,y,*a: x", Py_eval_input, globals.get(),
globals.get());
ASSERT_FALSE(o == NULL);
auto lambda = Take<PythonCallable>(o);
auto arginfo = lambda.GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 2);
EXPECT_EQ(arginfo.get().has_varargs, true);
EXPECT_EQ(arginfo.get().is_bound_method, false);
}
{
PyObject *o = PyRun_String("lambda x,y,*a,**kw: x", Py_eval_input,
globals.get(), globals.get());
ASSERT_FALSE(o == NULL);
auto lambda = Take<PythonCallable>(o);
auto arginfo = lambda.GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 2);
EXPECT_EQ(arginfo.get().has_varargs, true);
}
{
const char *script = R"(
class Foo:
def bar(self, x):
return x
bar_bound = Foo().bar
bar_unbound = Foo.bar
)";
PyObject *o =
PyRun_String(script, Py_file_input, globals.get(), globals.get());
ASSERT_FALSE(o == NULL);
Take<PythonObject>(o);
auto bar_bound = As<PythonCallable>(globals.GetItem("bar_bound"));
ASSERT_THAT_EXPECTED(bar_bound, llvm::Succeeded());
auto arginfo = bar_bound.get().GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 2); // FIXME, wrong
EXPECT_EQ(arginfo.get().has_varargs, false);
EXPECT_EQ(arginfo.get().is_bound_method, true);
auto bar_unbound = As<PythonCallable>(globals.GetItem("bar_unbound"));
ASSERT_THAT_EXPECTED(bar_unbound, llvm::Succeeded());
arginfo = bar_unbound.get().GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 2);
EXPECT_EQ(arginfo.get().has_varargs, false);
EXPECT_EQ(arginfo.get().is_bound_method, false);
}
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
// the old implementation of GetArgInfo just doesn't work on builtins.
{
auto builtins = PythonModule::BuiltinsModule();
auto hex = As<PythonCallable>(builtins.GetAttribute("hex"));
ASSERT_THAT_EXPECTED(hex, llvm::Succeeded());
auto arginfo = hex.get().GetArgInfo();
ASSERT_THAT_EXPECTED(arginfo, llvm::Succeeded());
EXPECT_EQ(arginfo.get().count, 1);
EXPECT_EQ(arginfo.get().has_varargs, false);
EXPECT_EQ(arginfo.get().is_bound_method, false);
}
#endif
}