From c86a6acaee55c98072ff06d372d049cb4a671fb5 Mon Sep 17 00:00:00 2001 From: Lawrence D'Anna Date: Thu, 17 Oct 2019 22:22:06 +0000 Subject: 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 --- .../ScriptInterpreter/Python/PythonDataObjects.h | 68 +++++++++++++++++----- 1 file changed, 53 insertions(+), 15 deletions(-) (limited to 'lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h') diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 255fa06..7f46023 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -309,20 +309,37 @@ protected: static llvm::Error exception(const char *s = nullptr) { return llvm::make_error(s); } + static llvm::Error keyError() { + return llvm::createStringError(llvm::inconvertibleErrorCode(), + "key not in dict"); + } + +#if PY_MAJOR_VERSION < 3 + // The python 2 API declares some arguments as char* that should + // be const char *, but it doesn't actually modify them. + static char *py2_const_cast(const char *s) { return const_cast(s); } +#else + static const char *py2_const_cast(const char *s) { return s; } +#endif public: template llvm::Expected CallMethod(const char *name, const T &... t) const { const char format[] = {'(', PythonFormat::format..., ')', 0}; -#if PY_MAJOR_VERSION < 3 - PyObject *obj = PyObject_CallMethod(m_py_obj, const_cast(name), - const_cast(format), - PythonFormat::get(t)...); -#else PyObject *obj = - PyObject_CallMethod(m_py_obj, name, format, PythonFormat::get(t)...); -#endif + PyObject_CallMethod(m_py_obj, py2_const_cast(name), + py2_const_cast(format), PythonFormat::get(t)...); + if (!obj) + return exception(); + return python::Take(obj); + } + + template + llvm::Expected Call(const T &... t) const { + const char format[] = {'(', PythonFormat::format..., ')', 0}; + PyObject *obj = PyObject_CallFunction(m_py_obj, py2_const_cast(format), + PythonFormat::get(t)...); if (!obj) return exception(); return python::Take(obj); @@ -386,6 +403,9 @@ template <> llvm::Expected As(llvm::Expected &&obj); template <> llvm::Expected As(llvm::Expected &&obj); +template <> +llvm::Expected As(llvm::Expected &&obj); + } // namespace python template class TypedPythonObject : public PythonObject { @@ -559,8 +579,14 @@ public: PythonList GetKeys() const; - PythonObject GetItemForKey(const PythonObject &key) const; - void SetItemForKey(const PythonObject &key, const PythonObject &value); + PythonObject GetItemForKey(const PythonObject &key) const; // DEPRECATED + void SetItemForKey(const PythonObject &key, + const PythonObject &value); // DEPRECATED + + llvm::Expected GetItem(const PythonObject &key) const; + llvm::Expected GetItem(const char *key) const; + llvm::Error SetItem(const PythonObject &key, const PythonObject &value) const; + llvm::Error SetItem(const char *key, const PythonObject &value) const; StructuredData::DictionarySP CreateStructuredDictionary() const; }; @@ -600,19 +626,31 @@ public: using TypedPythonObject::TypedPythonObject; struct ArgInfo { - size_t count; - bool is_bound_method : 1; - bool has_varargs : 1; - bool has_kwargs : 1; + /* the number of positional arguments, including optional ones, + * and excluding varargs. If this is a bound method, then the + * count will still include a +1 for self. + * + * FIXME. That's crazy. This should be replaced with + * an accurate min and max for positional args. + */ + int count; + /* does the callable have positional varargs? */ + bool has_varargs : 1; // FIXME delete this + /* is the callable a bound method written in python? */ + bool is_bound_method : 1; // FIXME delete this }; static bool Check(PyObject *py_obj); - ArgInfo GetNumArguments() const; + llvm::Expected GetArgInfo() const; + + llvm::Expected GetInitArgInfo() const; + + ArgInfo GetNumArguments() const; // DEPRECATED // If the callable is a Py_Class, then find the number of arguments // of the __init__ method. - ArgInfo GetNumInitArguments() const; + ArgInfo GetNumInitArguments() const; // DEPRECATED PythonObject operator()(); -- cgit v1.1