diff --git a/stackprinter/source_inspection.py b/stackprinter/source_inspection.py index 518e6ad..63bc52c 100644 --- a/stackprinter/source_inspection.py +++ b/stackprinter/source_inspection.py @@ -144,10 +144,19 @@ def _tokenize(source_lines): head_s = None head_e = None name_end = -2 + acceptable_multiline_tokens = [tokenize.STRING] + if hasattr(tokenize, "FSTRING_START"): + # we're >= python 3.12 + acceptable_multiline_tokens.extend([ + tokenize.FSTRING_START, + tokenize.FSTRING_MIDDLE, + tokenize.FSTRING_END]) + for ttype, string, (sline, scol), (eline, ecol), line in tokenizer: sline -= 1 # we deal in line indices counting from 0 eline -= 1 - if ttype != tokenize.STRING: + + if ttype not in acceptable_multiline_tokens: assert sline == eline, "Can't accept non-string multiline tokens" if ttype == tokenize.NAME: diff --git a/tests/source.py b/tests/source.py index 05bced3..64d4b1e 100644 --- a/tests/source.py +++ b/tests/source.py @@ -63,6 +63,12 @@ def spam_spam_spam(val): eels = "here is a "\ "multi line "\ "string" + + foo = f"""here is + {outer_scope_thing["and"]} + a multi-line f-string, + even with quotes in it + """ # np.reshape(bla, 9000) try: bla.nonexistant_attribute