Skip to content

Commit

Permalink
+ fixes #2417: console autocomplete runs python properties
Browse files Browse the repository at this point in the history
  • Loading branch information
wwmayer committed Jan 31, 2016
1 parent 6117c31 commit a3bf542
Showing 1 changed file with 41 additions and 7 deletions.
48 changes: 41 additions & 7 deletions src/Gui/CallTips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,11 @@ QString CallTipsList::extractContext(const QString& line) const
for (int i=0; i<len; i++) {
int pos = len-1-i;
const char ch = line.at(pos).toLatin1();
if ((ch >= 48 && ch <= 57) || // Numbers
(ch >= 65 && ch <= 90) || // Uppercase letters
(ch >= 97 && ch <= 122) || // Lowercase letters
(ch == '.') || (ch == '_'))
if ((ch >= 48 && ch <= 57) || // Numbers
(ch >= 65 && ch <= 90) || // Uppercase letters
(ch >= 97 && ch <= 122) || // Lowercase letters
(ch == '.') || (ch == '_') || // dot or underscore
(ch == ' ') || (ch == '\t')) // whitespace (between dot and text)
index = pos;
else
break;
Expand All @@ -203,9 +204,10 @@ QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
return tips;

try {
QStringList items = context.split(QLatin1Char('.'));
Py::Module module("__main__");
Py::Dict dict = module.getDict();
#if 0
QStringList items = context.split(QLatin1Char('.'));
QString modname = items.front();
items.pop_front();
if (!dict.hasKey(std::string(modname.toLatin1())))
Expand All @@ -222,7 +224,26 @@ QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
else
return tips;
}

#else
// Don't use hasattr & getattr because if a property is bound to a method this will be executed twice.
PyObject* code = Py_CompileString(static_cast<const char*>(context.toLatin1()), "<CallTipsList>", Py_eval_input);
if (!code) {
PyErr_Clear();
return tips;
}

PyObject* eval = 0;
if (PyCode_Check(code)) {
eval = PyEval_EvalCode(reinterpret_cast<PyCodeObject*>(code), dict.ptr(), dict.ptr());
}
Py_DECREF(code);
if (!eval) {
PyErr_Clear();
return tips;
}
Py::Object obj(eval, true);
#endif

// Checks whether the type is a subclass of PyObjectBase because to get the doc string
// of a member we must get it by its type instead of its instance otherwise we get the
// wrong string, namely that of the type of the member.
Expand All @@ -234,6 +255,8 @@ QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
Py::Object inst = obj; // the object instance
union PyType_Object typeobj = {&Base::PyObjectBase::Type};
union PyType_Object typedoc = {&App::DocumentObjectPy::Type};
union PyType_Object basetype = {&PyBaseObject_Type};

if (PyObject_IsSubclass(type.ptr(), typedoc.o) == 1) {
// From the template Python object we don't query its type object because there we keep
// a list of additional methods that we won't see otherwise. But to get the correct doc
Expand All @@ -252,7 +275,18 @@ QMap<QString, CallTip> CallTipsList::extractTips(const QString& context) const
PyObject* classobj = reinterpret_cast<PyObject*>(inst->in_class);
obj = Py::Object(classobj);
}
// TODO: How to find new style classes
else if (PyObject_IsInstance(obj.ptr(), basetype.o) == 1) {
// New style class which can be a module, type, list, tuple, int, float, ...
// Make sure it's not a type objec
union PyType_Object typetype = {&PyType_Type};
if (PyObject_IsInstance(obj.ptr(), typetype.o) != 1) {
// this should be now a user-defined Python class
// http://stackoverflow.com/questions/12233103/in-python-at-runtime-determine-if-an-object-is-a-class-old-and-new-type-instan
if (Py_TYPE(obj.ptr())->tp_flags & Py_TPFLAGS_HEAPTYPE) {
obj = type;
}
}
}

// If we have an instance of PyObjectBase then determine whether it's valid or not
if (PyObject_IsInstance(inst.ptr(), typeobj.o) == 1) {
Expand Down

0 comments on commit a3bf542

Please sign in to comment.