PY-36009 Add support of '=' for debugging in f-strings

GitOrigin-RevId: 79314512860158a4c535103ee2ac9fee43d30d0d
This commit is contained in:
Lada Gagina
2019-07-02 06:52:16 +03:00
committed by intellij-monorepo-bot
parent 3930aa3a6f
commit 3b991b20ca
11 changed files with 406 additions and 2 deletions
@@ -28,6 +28,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
@@ -446,7 +447,7 @@ public class PyBlock implements ASTBlock {
if (TreeUtil.findParent(child, PyElementTypes.FSTRING_FRAGMENT) != null) {
childWrap = Wrap.createWrap(WrapType.NONE, false);
}
return new PyBlock(this, child, childAlignment, childIndent, childWrap, myContext);
}
@@ -829,6 +830,10 @@ public class PyBlock implements ASTBlock {
final IElementType childType2 = psi2.getNode().getElementType();
child2 = getSubBlockByNode(node2);
if (isInsideFStringFragmentWithEqualsSign(myNode)) {
return Spacing.getReadOnlySpacing();
}
if ((childType1 == PyTokenTypes.EQ || childType2 == PyTokenTypes.EQ)) {
final PyNamedParameter namedParameter = as(myNode.getPsi(), PyNamedParameter.class);
if (namedParameter != null && namedParameter.getAnnotation() != null) {
@@ -1171,4 +1176,16 @@ public class PyBlock implements ASTBlock {
public boolean isLeaf() {
return myNode.getFirstChildNode() == null;
}
private static final TokenSet stopAtTokens = TokenSet.orSet(TokenSet.create(PyElementTypes.FSTRING_NODE),
PythonDialectsTokenSetProvider.INSTANCE.getStatementTokens());
private static boolean isInsideFStringFragmentWithEqualsSign(@NotNull ASTNode node) {
final ASTNode fStringFragmentParent = node.getElementType() == PyElementTypes.FSTRING_FRAGMENT
? node
: TreeUtil.findParent(node, TokenSet.create(PyElementTypes.FSTRING_FRAGMENT),
stopAtTokens);
if (fStringFragmentParent == null) return false;
return fStringFragmentParent.findChildByType(PyTokenTypes.EQ) != null;
}
}
@@ -172,7 +172,8 @@ public class ExpressionParsing extends Parsing {
PyTokenTypes.FSTRING_FRAGMENT_FORMAT_START,
PyTokenTypes.FSTRING_FRAGMENT_END,
PyTokenTypes.FSTRING_END,
PyTokenTypes.STATEMENT_BREAK)) {
PyTokenTypes.STATEMENT_BREAK,
PyTokenTypes.EQ)) {
nextToken();
recovery = true;
}
@@ -183,6 +184,8 @@ public class ExpressionParsing extends Parsing {
else {
recoveryMarker.drop();
}
matchToken(PyTokenTypes.EQ);
final boolean hasTypeConversion = matchToken(PyTokenTypes.FSTRING_FRAGMENT_TYPE_CONVERSION);
final boolean hasFormatPart = atToken(PyTokenTypes.FSTRING_FRAGMENT_FORMAT_START);
if (hasFormatPart) {
@@ -195,6 +198,7 @@ public class ExpressionParsing extends Parsing {
errorMessage = "type conversion, " + errorMessage;
}
}
checkMatches(PyTokenTypes.FSTRING_FRAGMENT_END, errorMessage);
marker.setCustomEdgeTokenBinders(null, CONSUME_COMMENTS_AND_SPACES_TO_LEFT);
marker.done(PyElementTypes.FSTRING_FRAGMENT);
@@ -691,4 +691,15 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
" not support positional-only parameters",
node);
}
@Override
public void visitPyFStringFragment(PyFStringFragment node) {
super.visitPyFStringFragment(node);
final ASTNode equalitySignInFStringFragment = node.getNode().findChildByType(PyTokenTypes.EQ);
if (equalitySignInFStringFragment != null) {
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON38) && registerForLanguageLevel(level),
" not support equality signs in f-strings", equalitySignInFStringFragment.getPsi());
}
}
}
@@ -0,0 +1,8 @@
print(f'{ math.pi = :.2f }')
s1 = f'{math.pi = :.2f}'
s2 = f'{f"{3.1415=:.1f}":*^20}'
s3 = f'{ 0 == 1 }'
s4 = f'{ 12345 + x = }'
s5 = f'''{
3
= }'''
@@ -0,0 +1,8 @@
print(f'{ math.pi = :.2f }')
s1 = f'{math.pi = :.2f}'
s2 = f'{f"{3.1415=:.1f}":*^20}'
s3 = f'{0 == 1}'
s4 = f'{ 12345 + x = }'
s5 = f'''{
3
= }'''
@@ -0,0 +1,3 @@
import math
print(<warning descr="Python version 2.6, 2.7, 3.4, 3.5 do not support a 'F' prefix">f</warning>'{math.pi <warning descr="Python version 2.6, 2.7, 3.4, 3.5, 3.6, 3.7 do not support equality signs in f-strings">=</warning>:.2f}')
print(<warning descr="Python version 2.6, 2.7, 3.4, 3.5 do not support a 'F' prefix">f</warning>'{<warning descr="Python version 2.6, 2.7, 3.4, 3.5 do not support a 'F' prefix">f</warning>"{3.1415<warning descr="Python version 2.6, 2.7, 3.4, 3.5, 3.6, 3.7 do not support equality signs in f-strings">=</warning>:.1f}":*^20}')
@@ -0,0 +1,19 @@
import math
s1 = f'{math.pi = :.2f}'
s2 = f'{f"{3.1415=:.1f}":*^20}'
s3 = f'{0 == 1}'
x = 'A string'
s4 = f'{x=!s}'
x = 2.71828
s5 = f'{x=:.2f}'
s6 = f'{x=:}'
s7 = f'{x=!r:^20}'
s8 = f'{x=!s:^20}'
s9 = f'{x= !a:^20}'
s10 = f'{3 * x + 15=}'
pi = 'π'
s11 = f'alpha α {pi = } ω omega'
s12 = f'''{
3
=}'''
s13 = f'{"="}'
+319
View File
@@ -0,0 +1,319 @@
PyFile:FStringEqualitySign.py
PyImportStatement
PsiElement(Py:IMPORT_KEYWORD)('import')
PsiWhiteSpace(' ')
PyImportElement:math
PyReferenceExpression: math
PsiElement(Py:IDENTIFIER)('math')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s1
PsiElement(Py:IDENTIFIER)('s1')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {math.pi = :.2f}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: pi
PyReferenceExpression: math
PsiElement(Py:IDENTIFIER)('math')
PsiElement(Py:DOT)('.')
PsiElement(Py:IDENTIFIER)('pi')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('.2f')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s2
PsiElement(Py:IDENTIFIER)('s2')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {f"{3.1415=:.1f}":*^20}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: {3.1415=:.1f}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f"')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyNumericLiteralExpression
PsiElement(Py:FLOAT_LITERAL)('3.1415')
PsiElement(Py:EQ)('=')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('.1f')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)('"')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('*^20')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s3
PsiElement(Py:IDENTIFIER)('s3')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {0 == 1}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyBinaryExpression
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('0')
PsiWhiteSpace(' ')
PsiElement(Py:EQEQ)('==')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('1')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: A string
PsiElement(Py:SINGLE_QUOTED_STRING)(''A string'')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s4
PsiElement(Py:IDENTIFIER)('s4')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {x=!s}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!s')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:FLOAT_LITERAL)('2.71828')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s5
PsiElement(Py:IDENTIFIER)('s5')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {x=:.2f}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('.2f')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s6
PsiElement(Py:IDENTIFIER)('s6')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {x=:}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s7
PsiElement(Py:IDENTIFIER)('s7')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {x=!r:^20}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!r')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('^20')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s8
PsiElement(Py:IDENTIFIER)('s8')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {x=!s:^20}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!s')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('^20')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s9
PsiElement(Py:IDENTIFIER)('s9')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {x= !a:^20}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PsiElement(Py:FSTRING_FRAGMENT_TYPE_CONVERSION)('!a')
PyFStringFragmentFormatPart
PsiElement(Py:FSTRING_FRAGMENT_FORMAT_START)(':')
PsiElement(Py:FSTRING_TEXT)('^20')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s10
PsiElement(Py:IDENTIFIER)('s10')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {3 * x + 15=}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyBinaryExpression
PyBinaryExpression
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('3')
PsiWhiteSpace(' ')
PsiElement(Py:MULT)('*')
PsiWhiteSpace(' ')
PyReferenceExpression: x
PsiElement(Py:IDENTIFIER)('x')
PsiWhiteSpace(' ')
PsiElement(Py:PLUS)('+')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('15')
PsiElement(Py:EQ)('=')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: pi
PsiElement(Py:IDENTIFIER)('pi')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: π
PsiElement(Py:SINGLE_QUOTED_STRING)(''π'')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s11
PsiElement(Py:IDENTIFIER)('s11')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: alpha α {pi = } ω omega
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PsiElement(Py:FSTRING_TEXT)('alpha α ')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyReferenceExpression: pi
PsiElement(Py:IDENTIFIER)('pi')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_TEXT)(' ω omega')
PsiElement(Py:FSTRING_END)(''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s12
PsiElement(Py:IDENTIFIER)('s12')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {
3
=}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'''')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PsiWhiteSpace('\n ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('3')
PsiWhiteSpace('\n ')
PsiElement(Py:EQ)('=')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''''')
PsiWhiteSpace('\n')
PyAssignmentStatement
PyTargetExpression: s13
PsiElement(Py:IDENTIFIER)('s13')
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PyStringLiteralExpression: {"="}
PyFormattedStringElement
PsiElement(Py:FSTRING_START)('f'')
PyFStringFragment
PsiElement(Py:FSTRING_FRAGMENT_START)('{')
PyStringLiteralExpression: =
PsiElement(Py:SINGLE_QUOTED_STRING)('"="')
PsiElement(Py:FSTRING_FRAGMENT_END)('}')
PsiElement(Py:FSTRING_END)(''')
@@ -954,4 +954,9 @@ public class PyFormatterTest extends PyTestCase {
public void testSpacesAroundFStringFragmentExpressionStripped() {
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest);
}
// PY-36009
public void testSpacesAroundEqualsSignInFStringFragment() {
doTest();
}
}
@@ -901,6 +901,11 @@ public class PythonParsingTest extends ParsingTestCase {
doTest(LanguageLevel.PYTHON38);
}
// PY-36009
public void testFStringEqualitySign() {
doTest(LanguageLevel.PYTHON37);
}
public void doTest() {
doTest(LanguageLevel.PYTHON26);
}
@@ -215,6 +215,11 @@ public class PyCompatibilityInspectionTest extends PyInspectionTestCase {
doTest(LanguageLevel.PYTHON34);
}
// PY-36009
public void testEqualitySignInFStrings() {
doTest(LanguageLevel.PYTHON38);
}
public void testInputFromSixLib() {
doTest(LanguageLevel.PYTHON27);
}