Support six.add_metaclass decorator

This commit is contained in:
Semyon Proshev
2016-11-23 14:37:35 +03:00
parent a0d7707e84
commit 919ba06dd7
2 changed files with 77 additions and 0 deletions
@@ -1455,9 +1455,58 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
}
}
final PyDecoratorList decoratorList = getDecoratorList();
if (decoratorList != null) {
for (PyDecorator decorator : decoratorList.getDecorators()) {
if (isSixAddMetaclass(decorator)) {
final PyExpression[] arguments = decorator.getArguments();
if (arguments.length != 0) {
return arguments[0];
}
}
}
}
return null;
}
private static boolean isSixAddMetaclass(@NotNull PyDecorator decorator) {
final PyExpression callee = decorator.getCallee();
if (callee != null && "add_metaclass".equals(callee.getName())) {
// SUPPORTED CASES:
// import six
// six.add_metaclass(...)
// from six import add_metaclass
// add_metaclass(...)
return true;
}
if (callee instanceof PyReferenceExpression) {
// SUPPORTED CASES:
// from six import add_metaclass as a_m
// a_m(...)
final boolean importedAddMetaclass = StreamEx
.of(PyResolveUtil.resolveLocally((PyReferenceExpression)callee))
.select(PyImportElement.class)
.map(PyImportElement::getImportedQName)
.nonNull()
.map(QualifiedName::getLastComponent)
.nonNull()
.findAny("add_metaclass"::equals)
.isPresent();
if (importedAddMetaclass) {
return true;
}
}
return false;
}
@NotNull
private List<PyClassLikeType> getMROAncestorTypes(@NotNull TypeEvalContext context) throws MROException {
PyPsiUtils.assertValid(this);
@@ -1076,6 +1076,34 @@ public class PythonCompletionTest extends PyTestCase {
});
}
public void testSixAddMetaclass() {
final List<String> suggested = doTestByText("import six\n" +
"class M(type):\n" +
" def baz(self):\n" +
" pass\n" +
"@six.add_metaclass(M)\n" +
"class C(object):\n" +
" def foo(self):\n" +
" C.ba<caret>()");
assertNotNull(suggested);
assertContainsElements(suggested, "baz");
}
public void testSixAddMetaclassWithAs() {
final List<String> suggested = doTestByText("from six import add_metaclass as a_m\n" +
"class M(type):\n" +
" def baz(self):\n" +
" pass\n" +
"@a_m(M)\n" +
"class C(object):\n" +
" def foo(self):\n" +
" C.ba<caret>()");
assertNotNull(suggested);
assertContainsElements(suggested, "baz");
}
@Override
protected String getTestDataPath() {
return super.getTestDataPath() + "/completion";