diff --git a/test/import/python/types.py b/test/import/python/types.py index 5203f90fa..2290f8299 100644 --- a/test/import/python/types.py +++ b/test/import/python/types.py @@ -1,32 +1,50 @@ # coding=UTF-8 # see https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables +class ReferencedType(): + def __init__(self, a=0, b=0): + pass + +myobject = ReferencedType() + class Types(): class_variable_bool_true=True class_variable_bool_false=False class_variable_int=3 class_variable_negative_int=-54341 class_variable_float=3.12334 class_variable_negative_float=-1.0876544 class_variable_string="1234" class_variable_list=[] + class_variable_list_params=[1,2] class_variable_dict={} - class_variable_object=bpy.types.render + class_variable_dict_params={'a':1, 'b':2} + class_variable_tuple=() + class_variable_tuple_params=(1,2,3) + class_variable_object=myobject + class_variable_object_type=ReferencedType() + class_variable_object_type_params=ReferencedType(1,2) # We import instance variables only from __init__() def __init__(): self.instance_variable_bool_true=True self.instance_variable_bool_false=False self.instance_variable_int=3 self.instance_variable_negative_int=-54341 self.instance_variable_float=3.12334 self.instance_variable_negative_float=-1.9876 self.test() # should be ignored self.instance_variable_string="1234" self.instance_variable_list=[] + self.instance_variable_list_params=[1,2] self.instance_variable_dict={} - self.instance_variable_object=bpy.types.render + self.instance_variable_dict_params={'a':1, 'b':2} + self.instance_variable_tuples=() + self.instance_variable_tuple_params=(1,2,3) + self.instance_variable_object=myobject + self.instance_variable_object_type=ReferencedType() + self.instance_variable_object_type_params=ReferencedType(1,2) def test(): - pass \ No newline at end of file + pass diff --git a/umbrello/codeimport/pythonimport.cpp b/umbrello/codeimport/pythonimport.cpp index 3d48e73f7..7336db0ce 100644 --- a/umbrello/codeimport/pythonimport.cpp +++ b/umbrello/codeimport/pythonimport.cpp @@ -1,403 +1,416 @@ /*************************************************************************** * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2006-2014 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "pythonimport.h" // app includes #include "attribute.h" #include "classifier.h" #include "codeimpthread.h" #include "debug_utils.h" #include "enum.h" #include "import_utils.h" #include "operation.h" #include "package.h" #include "uml.h" #include "umldoc.h" #include "umlpackagelist.h" // qt includes #include /** * Constructor. */ PythonImport::PythonImport(CodeImpThread* thread) : NativeImportBase(QLatin1String("#"), thread) { setMultiLineComment(QLatin1String("\"\"\""), QLatin1String("\"\"\"")); initVars(); } /** * Destructor. */ PythonImport::~PythonImport() { } /** * Reimplement operation from NativeImportBase. */ void PythonImport::initVars() { m_srcIndentIndex = 0; m_srcIndent[m_srcIndentIndex] = 0; m_braceWasOpened = false; m_isStatic = false; } /** * Reimplement operation from NativeImportBase. * In addition to handling multiline comments, this method transforms * changes in leading indentation into braces (opening brace for increase * in indentation, closing brace for decrease in indentation) in m_source. * Removal of Python's indentation sensitivity simplifies subsequent * processing using Umbrello's native import framework. * @param line the line to preprocess * @return success status of operation */ bool PythonImport::preprocess(QString& line) { if (NativeImportBase::preprocess(line)) return true; // Handle single line comment int pos = line.indexOf(m_singleLineCommentIntro); if (pos != -1) { QString cmnt = line.mid(pos); m_source.append(cmnt); m_srcIndex++; if (pos == 0) return true; line = line.left(pos); line.remove(QRegExp(QLatin1String("\\s+$"))); } // Transform changes in indentation into braces a la C++/Java/Perl/... pos = line.indexOf(QRegExp(QLatin1String("\\S"))); if (pos == -1) return true; bool isContinuation = false; int leadingWhite = line.left(pos).count(QRegExp(QLatin1String("\\s"))); if (leadingWhite > m_srcIndent[m_srcIndentIndex]) { if (m_srcIndex == 0) { uError() << "internal error"; return true; } if (m_braceWasOpened) { m_srcIndent[++m_srcIndentIndex] = leadingWhite; m_braceWasOpened = false; } else { isContinuation = true; } } else { while (m_srcIndentIndex > 0 && leadingWhite < m_srcIndent[m_srcIndentIndex]) { m_srcIndentIndex--; m_source.append(QLatin1String("}")); m_srcIndex++; } } if (m_braceWasOpened && m_srcIndentIndex == 0) { m_source.append(QLatin1String("}")); m_srcIndex++; } if (line.endsWith(QLatin1Char(':'))) { line.replace(QRegExp(QLatin1String(":$")), QLatin1String("{")); m_braceWasOpened = true; } else { m_braceWasOpened = false; } if (!isContinuation && !m_braceWasOpened) line += QLatin1Char(';'); return false; // The input was not completely consumed by preprocessing. } /** * Implement abstract operation from NativeImportBase. * @param word whitespace delimited item */ void PythonImport::fillSource(const QString& word) { QString lexeme; const uint len = word.length(); for (uint i = 0; i < len; ++i) { const QChar& c = word[i]; if (c.isLetterOrNumber() || c == QLatin1Char('_') || c == QLatin1Char('.')) { lexeme += c; } else { if (!lexeme.isEmpty()) { m_source.append(lexeme); m_srcIndex++; lexeme.clear(); } m_source.append(QString(c)); m_srcIndex++; } } if (!lexeme.isEmpty()) { m_source.append(lexeme); m_srcIndex++; } } /** * Return an amount of spaces that corresponds to @param level * @return spaces of indentation */ QString PythonImport::indentation(int level) { QString spaces; for (int i = 0; i < level; ++i) { spaces += QLatin1String(" "); } return spaces; } /** * Skip ahead to outermost closing brace. * @return body contents skipped */ QString PythonImport::skipBody() { /* During input preprocessing, changes in indentation were replaced by braces, and a semicolon was appended to each line ending. In order to return the body, we try to reconstruct the original Python syntax by reverting those changes. */ QString body; if (m_source[m_srcIndex] != QLatin1String("{")) skipStmt(QLatin1String("{")); bool firstTokenAfterNewline = true; int braceNesting = 0; QString token; while (!(token = advance()).isNull()) { if (token == QLatin1String("}")) { if (braceNesting <= 0) break; braceNesting--; body += QLatin1Char('\n'); firstTokenAfterNewline = true; } else if (token == QLatin1String("{")) { braceNesting++; body += QLatin1String(":\n"); firstTokenAfterNewline = true; } else if (token == QLatin1String(";")) { body += QLatin1Char('\n'); firstTokenAfterNewline = true; } else { if (firstTokenAfterNewline) { body += indentation(braceNesting); firstTokenAfterNewline = false; } else if (body.contains(QRegExp(QLatin1String("\\w$"))) && token.contains(QRegExp(QLatin1String("^\\w")))) { body += QLatin1Char(' '); } body += token; } } return body; } /** * Parse assignments in the form \ '=' \ * Instance variables are identified by a prefixed 'self.'. * @return success status of parsing */ bool PythonImport::parseAssignmentStmt(const QString keyword) { QString variable = keyword; advance(); QString value = advance(); if (value == QLatin1String("-")) value.append(advance()); bool isStatic = true; if (variable.startsWith(QLatin1String("self."))) { variable.remove(0,5); isStatic = false; } Uml::Visibility::Enum visibility = Uml::Visibility::Public; if (variable.startsWith(QLatin1String("__"))) { visibility = Uml::Visibility::Private; variable.remove(0, 2); } else if (variable.startsWith(QLatin1String("_"))) { visibility = Uml::Visibility::Protected; variable.remove(0, 1); } QString type; if (value == QLatin1String("[")) { - if (lookAhead() == QLatin1String("]")) { - advance(); - type = QLatin1String("list"); - value = QLatin1String(""); + type = QLatin1String("list"); + for(QString token = advance(); token != ";"; token = advance()) { + value += token; } } else if (value == QLatin1String("{")) { - if (lookAhead() == QLatin1String("}")) { - advance(); - type = QLatin1String("dict"); - value = QLatin1String(""); + type = QLatin1String("dict"); + for(QString token = advance(); token != ";"; token = advance()) { + value += token; + } + } else if (value == QLatin1String("(")) { + type = QLatin1String("tuple"); + for(QString token = advance(); token != ";"; token = advance()) { + value += token; } } else if (value.startsWith(QLatin1String("\""))) { type = QLatin1String("string"); } else if (value == QLatin1String("True") || value == QLatin1String("False")) { type = QLatin1String("bool"); } else if (value.contains(QRegExp(QLatin1String("-?\\d+\\.\\d*")))) { type = QLatin1String("float"); } else if (value.contains(QRegExp(QLatin1String("-?\\d+")))) { type = QLatin1String("int"); } else if (!value.isEmpty()) { - type = QLatin1String("object"); + if (lookAhead() == "(") { + advance(); + type = value; + value = QLatin1String(""); + for(QString token = advance(); token != ")"; token = advance()) { + value += token; + } + if (!value.isEmpty()) + value = QString("(%1)").arg(value); + } else + type = QLatin1String("object"); } UMLObject* o = Import_Utils::insertAttribute(m_klass, visibility, variable, type, m_comment, false); UMLAttribute* a = o->asUMLAttribute(); a->setInitialValue(value); a->setStatic(isStatic); return true; } /** * Implement abstract operation from NativeImportBase. * @return success status of operation */ bool PythonImport::parseStmt() { const int srcLength = m_source.count(); QString keyword = m_source[m_srcIndex]; if (keyword == QLatin1String("class")) { const QString& name = advance(); UMLObject *ns = Import_Utils::createUMLObject(UMLObject::ot_Class, name, currentScope(), m_comment); pushScope(m_klass = ns->asUMLClassifier()); m_comment.clear(); if (advance() == QLatin1String("(")) { while (m_srcIndex < srcLength - 1 && advance() != QLatin1String(")")) { const QString& baseName = m_source[m_srcIndex]; Import_Utils::createGeneralization(m_klass, baseName); if (advance() != QLatin1String(",")) break; } } if (m_source[m_srcIndex] != QLatin1String("{")) { skipStmt(QLatin1String("{")); } log(QLatin1String("class ") + name); return true; } if (keyword == QLatin1String("@")) { const QString& annotation = m_source[++m_srcIndex]; uDebug() << "annotation:" << annotation; if (annotation == QLatin1String("staticmethod")) m_isStatic = true; return true; } if (keyword == QLatin1String("def")) { if (m_klass == 0) { // skip functions outside of a class skipBody(); return true; } if (!m_klass->hasDoc() && !m_comment.isEmpty()) { m_klass->setDoc(m_comment); m_comment = QString(); } QString name = advance(); bool isConstructor = name == QLatin1String("__init__"); Uml::Visibility::Enum visibility = Uml::Visibility::Public; if (!isConstructor) { if (name.startsWith(QLatin1String("__"))) { name = name.mid(2); visibility = Uml::Visibility::Private; } else if (name.startsWith(QLatin1String("_"))) { name = name.mid(1); visibility = Uml::Visibility::Protected; } } UMLOperation *op = Import_Utils::makeOperation(m_klass, name); if (advance() != QLatin1String("(")) { uError() << "importPython def " << name << ": expecting \"(\""; skipBody(); return true; } bool firstParam = true; while (m_srcIndex < srcLength && advance() != QLatin1String(")")) { const QString& parName = m_source[m_srcIndex]; if (firstParam) { if (parName.compare(QLatin1String("self"), Qt::CaseInsensitive) != 0) { m_isStatic = true; Import_Utils::addMethodParameter(op, QLatin1String("string"), parName); } firstParam = false; } else { /*UMLAttribute *att =*/ Import_Utils::addMethodParameter(op, QLatin1String("string"), parName); } if (advance() != QLatin1String(",")) break; } Import_Utils::insertMethod(m_klass, op, visibility, QLatin1String("string"), m_isStatic, false /*isAbstract*/, false /*isFriend*/, isConstructor, false, m_comment); m_isStatic = false; int srcIndex = m_srcIndex; op->setSourceCode(skipBody()); if (!op->hasDoc() && !m_comment.isEmpty()) { op->setDoc(m_comment); m_comment = QString(); } // parse instance variables from __init__ method if (isConstructor) { int indexSave = m_srcIndex; m_srcIndex = srcIndex; advance(); keyword = advance(); while (m_srcIndex < indexSave) { if (lookAhead() == QLatin1String("=")) { parseAssignmentStmt(keyword); // skip ; inserted by lexer if (lookAhead() == QLatin1String(";")) { advance(); keyword = advance(); } } else { skipStmt(QLatin1String(";")); keyword = advance(); } } m_srcIndex = indexSave; } log(QLatin1String("def ") + name); return true; } // parse class variables if (m_klass && lookAhead() == QLatin1String("=")) { bool result = parseAssignmentStmt(keyword); log(QLatin1String("class attribute ") + keyword); return result; } if (keyword == QLatin1String("}")) { if (scopeIndex()) { m_klass = popScope()->asUMLClassifier(); } else uError() << "parsing: too many }"; return true; } return false; // @todo parsing of attributes }