summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2023-07-06 09:10:18 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-07-10 13:26:42 +0000
commit898a816744cd1fb421082e2e0cf6d49ed70a851d (patch)
treea2016ae282d89ae7e20a0032de044199f5eeb82d
parent38349b4fdd18f958a99356f18849aecdceed7d1f (diff)
lupdate/Python: Handle f/r strings
Fixes: PYSIDE-2380 Change-Id: I44ed60039af4df82d4f06bfae9e229847ad1fad6 Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io> (cherry picked from commit 5577432ba554e4835ceaa76f6bd97d76f6b7e8aa) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/linguist/lupdate/python.cpp51
-rw-r--r--tests/auto/linguist/lupdate/testdata/good/parsepython/main.py4
-rw-r--r--tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result22
3 files changed, 68 insertions, 9 deletions
diff --git a/src/linguist/lupdate/python.cpp b/src/linguist/lupdate/python.cpp
index 1cadadc59..0bc3bf5e8 100644
--- a/src/linguist/lupdate/python.cpp
+++ b/src/linguist/lupdate/python.cpp
@@ -30,6 +30,14 @@ enum Token { Tok_Eof, Tok_class, Tok_def, Tok_return, Tok_tr,
Tok_LeftParen, Tok_RightParen,
Tok_Comma, Tok_None, Tok_Integer};
+enum class StringType
+{
+ NoString,
+ String,
+ FormatString,
+ RawString
+};
+
/*
The tokenizer maintains the following global variables. The names
should be self-explanatory.
@@ -125,7 +133,7 @@ static void startTokenizer(const QString &fileName, int (*getCharFunc)(),
yyContextStack.clear();
}
-static bool parseStringEscape()
+static bool parseStringEscape(int quoteChar, StringType stringType)
{
static const char tab[] = "abfnrtv";
static const char backTab[] = "\a\b\f\n\r\t\v";
@@ -134,6 +142,14 @@ static bool parseStringEscape()
if (yyCh == EOF)
return false;
+ if (stringType == StringType::RawString) {
+ if (yyCh != quoteChar) // Only quotes can be escaped in raw strings
+ yyString[yyStringLen++] = '\\';
+ yyString[yyStringLen++] = yyCh;
+ yyCh = getChar();
+ return true;
+ }
+
if (yyCh == 'x') {
QByteArray hex = "0";
yyCh = getChar();
@@ -185,7 +201,7 @@ static bool parseStringEscape()
return true;
}
-static Token parseString()
+static Token parseString(StringType stringType = StringType::NoString)
{
int quoteChar = yyCh;
bool tripleQuote = false;
@@ -226,7 +242,7 @@ static Token parseString()
}
if (yyCh == '\\') {
- if (!parseStringEscape())
+ if (!parseStringEscape(quoteChar, stringType))
return Tok_Eof;
} else {
char *yStart = yyString + yyStringLen;
@@ -266,7 +282,7 @@ static QByteArray readLine()
return result;
}
-static Token getToken()
+static Token getToken(StringType stringType = StringType::NoString)
{
yyIdent.clear();
yyCommentLen = 0;
@@ -304,7 +320,7 @@ static Token getToken()
break;
case '"':
case '\'':
- return parseString();
+ return parseString(stringType);
case '(':
yyParenDepth++;
yyCh = getChar();
@@ -376,15 +392,34 @@ static bool match(Token t)
return matches;
}
+static bool matchStringStart()
+{
+ if (yyTok == Tok_String)
+ return true;
+ // Check for f"bla{var}" and raw strings r"bla".
+ if (yyTok == Tok_Ident && yyIdent.size() == 1) {
+ switch (yyIdent.at(0)) {
+ case 'r':
+ yyTok = getToken(StringType::RawString);
+ return yyTok == Tok_String;
+ case 'f':
+ yyTok = getToken(StringType::FormatString);
+ return yyTok == Tok_String;
+ }
+ }
+ return false;
+}
+
static bool matchString(QByteArray *s)
{
- const bool matches = (yyTok == Tok_String);
s->clear();
- while (yyTok == Tok_String) {
+ bool ok = false;
+ while (matchStringStart()) {
*s += yyString;
yyTok = getToken();
+ ok = true;
}
- return matches;
+ return ok;
}
static bool matchEncoding(bool *utf8)
diff --git a/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py b/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py
index e2bf9ac93..5c0c77851 100644
--- a/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py
+++ b/tests/auto/linguist/lupdate/testdata/good/parsepython/main.py
@@ -72,6 +72,10 @@ class Window(QMainWindow):
def window_method(self): # PYSIDE-2379, Don't put this into NestedClass
msg = self.tr("Window Message")
+ msg = self.tr(f"An f-string\\")
+ msg = self.tr(r"A raw strin\g")
+ msg = self.tr(r"A raw strin\g""continued\\")
+ msg = self.tr(r"A raw string with escaped quote\"bla")
if __name__ == '__main__':
diff --git a/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result
index 658485339..df1788672 100644
--- a/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result
+++ b/tests/auto/linguist/lupdate/testdata/good/parsepython/project.ts.result
@@ -81,9 +81,29 @@
</translation>
</message>
<message>
- <location filename="main.py" line="77"/>
+ <location filename="main.py" line="75"/>
<source>Window Message</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="main.py" line="76"/>
+ <source>An f-string\</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="main.py" line="77"/>
+ <source>A raw strin\g</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="main.py" line="78"/>
+ <source>A raw strin\gcontinued\</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="main.py" line="81"/>
+ <source>A raw string with escaped quote&quot;bla</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>