bpo-30465: Fix lineno and col_offset in fstring AST nodes (GH-1800) (… · python/cpython@aa1afc7 (original) (raw)

`@@ -70,6 +70,253 @@ def call(self):

`

70

70

`# Make sure x was called.

`

71

71

`self.assertTrue(x.called)

`

72

72

``

``

73

`+

def test_ast_line_numbers(self):

`

``

74

`+

expr = """

`

``

75

`+

a = 10

`

``

76

`+

f'{a * x()}'"""

`

``

77

`+

t = ast.parse(expr)

`

``

78

`+

self.assertEqual(type(t), ast.Module)

`

``

79

`+

self.assertEqual(len(t.body), 2)

`

``

80

`` +

check a = 10

``

``

81

`+

self.assertEqual(type(t.body[0]), ast.Assign)

`

``

82

`+

self.assertEqual(t.body[0].lineno, 2)

`

``

83

`` +

check f'...'

``

``

84

`+

self.assertEqual(type(t.body[1]), ast.Expr)

`

``

85

`+

self.assertEqual(type(t.body[1].value), ast.JoinedStr)

`

``

86

`+

self.assertEqual(len(t.body[1].value.values), 1)

`

``

87

`+

self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)

`

``

88

`+

self.assertEqual(t.body[1].lineno, 3)

`

``

89

`+

self.assertEqual(t.body[1].value.lineno, 3)

`

``

90

`+

self.assertEqual(t.body[1].value.values[0].lineno, 3)

`

``

91

`+

check the binop location

`

``

92

`+

binop = t.body[1].value.values[0].value

`

``

93

`+

self.assertEqual(type(binop), ast.BinOp)

`

``

94

`+

self.assertEqual(type(binop.left), ast.Name)

`

``

95

`+

self.assertEqual(type(binop.op), ast.Mult)

`

``

96

`+

self.assertEqual(type(binop.right), ast.Call)

`

``

97

`+

self.assertEqual(binop.lineno, 3)

`

``

98

`+

self.assertEqual(binop.left.lineno, 3)

`

``

99

`+

self.assertEqual(binop.right.lineno, 3)

`

``

100

`+

self.assertEqual(binop.col_offset, 3)

`

``

101

`+

self.assertEqual(binop.left.col_offset, 3)

`

``

102

`+

self.assertEqual(binop.right.col_offset, 7)

`

``

103

+

``

104

`+

def test_ast_line_numbers_multiple_formattedvalues(self):

`

``

105

`+

expr = """

`

``

106

`+

f'no formatted values'

`

``

107

`+

f'eggs {a * x()} spam {b + y()}'"""

`

``

108

`+

t = ast.parse(expr)

`

``

109

`+

self.assertEqual(type(t), ast.Module)

`

``

110

`+

self.assertEqual(len(t.body), 2)

`

``

111

`` +

check f'no formatted value'

``

``

112

`+

self.assertEqual(type(t.body[0]), ast.Expr)

`

``

113

`+

self.assertEqual(type(t.body[0].value), ast.JoinedStr)

`

``

114

`+

self.assertEqual(t.body[0].lineno, 2)

`

``

115

`` +

check f'...'

``

``

116

`+

self.assertEqual(type(t.body[1]), ast.Expr)

`

``

117

`+

self.assertEqual(type(t.body[1].value), ast.JoinedStr)

`

``

118

`+

self.assertEqual(len(t.body[1].value.values), 4)

`

``

119

`+

self.assertEqual(type(t.body[1].value.values[0]), ast.Str)

`

``

120

`+

self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)

`

``

121

`+

self.assertEqual(type(t.body[1].value.values[2]), ast.Str)

`

``

122

`+

self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue)

`

``

123

`+

self.assertEqual(t.body[1].lineno, 3)

`

``

124

`+

self.assertEqual(t.body[1].value.lineno, 3)

`

``

125

`+

self.assertEqual(t.body[1].value.values[0].lineno, 3)

`

``

126

`+

self.assertEqual(t.body[1].value.values[1].lineno, 3)

`

``

127

`+

self.assertEqual(t.body[1].value.values[2].lineno, 3)

`

``

128

`+

self.assertEqual(t.body[1].value.values[3].lineno, 3)

`

``

129

`+

check the first binop location

`

``

130

`+

binop1 = t.body[1].value.values[1].value

`

``

131

`+

self.assertEqual(type(binop1), ast.BinOp)

`

``

132

`+

self.assertEqual(type(binop1.left), ast.Name)

`

``

133

`+

self.assertEqual(type(binop1.op), ast.Mult)

`

``

134

`+

self.assertEqual(type(binop1.right), ast.Call)

`

``

135

`+

self.assertEqual(binop1.lineno, 3)

`

``

136

`+

self.assertEqual(binop1.left.lineno, 3)

`

``

137

`+

self.assertEqual(binop1.right.lineno, 3)

`

``

138

`+

self.assertEqual(binop1.col_offset, 8)

`

``

139

`+

self.assertEqual(binop1.left.col_offset, 8)

`

``

140

`+

self.assertEqual(binop1.right.col_offset, 12)

`

``

141

`+

check the second binop location

`

``

142

`+

binop2 = t.body[1].value.values[3].value

`

``

143

`+

self.assertEqual(type(binop2), ast.BinOp)

`

``

144

`+

self.assertEqual(type(binop2.left), ast.Name)

`

``

145

`+

self.assertEqual(type(binop2.op), ast.Add)

`

``

146

`+

self.assertEqual(type(binop2.right), ast.Call)

`

``

147

`+

self.assertEqual(binop2.lineno, 3)

`

``

148

`+

self.assertEqual(binop2.left.lineno, 3)

`

``

149

`+

self.assertEqual(binop2.right.lineno, 3)

`

``

150

`+

self.assertEqual(binop2.col_offset, 23)

`

``

151

`+

self.assertEqual(binop2.left.col_offset, 23)

`

``

152

`+

self.assertEqual(binop2.right.col_offset, 27)

`

``

153

+

``

154

`+

def test_ast_line_numbers_nested(self):

`

``

155

`+

expr = """

`

``

156

`+

a = 10

`

``

157

`+

f'{a * f"-{x()}-"}'"""

`

``

158

`+

t = ast.parse(expr)

`

``

159

`+

self.assertEqual(type(t), ast.Module)

`

``

160

`+

self.assertEqual(len(t.body), 2)

`

``

161

`` +

check a = 10

``

``

162

`+

self.assertEqual(type(t.body[0]), ast.Assign)

`

``

163

`+

self.assertEqual(t.body[0].lineno, 2)

`

``

164

`` +

check f'...'

``

``

165

`+

self.assertEqual(type(t.body[1]), ast.Expr)

`

``

166

`+

self.assertEqual(type(t.body[1].value), ast.JoinedStr)

`

``

167

`+

self.assertEqual(len(t.body[1].value.values), 1)

`

``

168

`+

self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)

`

``

169

`+

self.assertEqual(t.body[1].lineno, 3)

`

``

170

`+

self.assertEqual(t.body[1].value.lineno, 3)

`

``

171

`+

self.assertEqual(t.body[1].value.values[0].lineno, 3)

`

``

172

`+

check the binop location

`

``

173

`+

binop = t.body[1].value.values[0].value

`

``

174

`+

self.assertEqual(type(binop), ast.BinOp)

`

``

175

`+

self.assertEqual(type(binop.left), ast.Name)

`

``

176

`+

self.assertEqual(type(binop.op), ast.Mult)

`

``

177

`+

self.assertEqual(type(binop.right), ast.JoinedStr)

`

``

178

`+

self.assertEqual(binop.lineno, 3)

`

``

179

`+

self.assertEqual(binop.left.lineno, 3)

`

``

180

`+

self.assertEqual(binop.right.lineno, 3)

`

``

181

`+

self.assertEqual(binop.col_offset, 3)

`

``

182

`+

self.assertEqual(binop.left.col_offset, 3)

`

``

183

`+

self.assertEqual(binop.right.col_offset, 7)

`

``

184

`+

check the nested call location

`

``

185

`+

self.assertEqual(len(binop.right.values), 3)

`

``

186

`+

self.assertEqual(type(binop.right.values[0]), ast.Str)

`

``

187

`+

self.assertEqual(type(binop.right.values[1]), ast.FormattedValue)

`

``

188

`+

self.assertEqual(type(binop.right.values[2]), ast.Str)

`

``

189

`+

self.assertEqual(binop.right.values[0].lineno, 3)

`

``

190

`+

self.assertEqual(binop.right.values[1].lineno, 3)

`

``

191

`+

self.assertEqual(binop.right.values[2].lineno, 3)

`

``

192

`+

call = binop.right.values[1].value

`

``

193

`+

self.assertEqual(type(call), ast.Call)

`

``

194

`+

self.assertEqual(call.lineno, 3)

`

``

195

`+

self.assertEqual(call.col_offset, 11)

`

``

196

+

``

197

`+

def test_ast_line_numbers_duplicate_expression(self):

`

``

198

`+

"""Duplicate expression

`

``

199

+

``

200

`+

NOTE: this is currently broken, always sets location of the first

`

``

201

`+

expression.

`

``

202

`+

"""

`

``

203

`+

expr = """

`

``

204

`+

a = 10

`

``

205

`+

f'{a * x()} {a * x()} {a * x()}'

`

``

206

`+

"""

`

``

207

`+

t = ast.parse(expr)

`

``

208

`+

self.assertEqual(type(t), ast.Module)

`

``

209

`+

self.assertEqual(len(t.body), 2)

`

``

210

`` +

check a = 10

``

``

211

`+

self.assertEqual(type(t.body[0]), ast.Assign)

`

``

212

`+

self.assertEqual(t.body[0].lineno, 2)

`

``

213

`` +

check f'...'

``

``

214

`+

self.assertEqual(type(t.body[1]), ast.Expr)

`

``

215

`+

self.assertEqual(type(t.body[1].value), ast.JoinedStr)

`

``

216

`+

self.assertEqual(len(t.body[1].value.values), 5)

`

``

217

`+

self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue)

`

``

218

`+

self.assertEqual(type(t.body[1].value.values[1]), ast.Str)

`

``

219

`+

self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue)

`

``

220

`+

self.assertEqual(type(t.body[1].value.values[3]), ast.Str)

`

``

221

`+

self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue)

`

``

222

`+

self.assertEqual(t.body[1].lineno, 3)

`

``

223

`+

self.assertEqual(t.body[1].value.lineno, 3)

`

``

224

`+

self.assertEqual(t.body[1].value.values[0].lineno, 3)

`

``

225

`+

self.assertEqual(t.body[1].value.values[1].lineno, 3)

`

``

226

`+

self.assertEqual(t.body[1].value.values[2].lineno, 3)

`

``

227

`+

self.assertEqual(t.body[1].value.values[3].lineno, 3)

`

``

228

`+

self.assertEqual(t.body[1].value.values[4].lineno, 3)

`

``

229

`+

check the first binop location

`

``

230

`+

binop = t.body[1].value.values[0].value

`

``

231

`+

self.assertEqual(type(binop), ast.BinOp)

`

``

232

`+

self.assertEqual(type(binop.left), ast.Name)

`

``

233

`+

self.assertEqual(type(binop.op), ast.Mult)

`

``

234

`+

self.assertEqual(type(binop.right), ast.Call)

`

``

235

`+

self.assertEqual(binop.lineno, 3)

`

``

236

`+

self.assertEqual(binop.left.lineno, 3)

`

``

237

`+

self.assertEqual(binop.right.lineno, 3)

`

``

238

`+

self.assertEqual(binop.col_offset, 3)

`

``

239

`+

self.assertEqual(binop.left.col_offset, 3)

`

``

240

`+

self.assertEqual(binop.right.col_offset, 7)

`

``

241

`+

check the second binop location

`

``

242

`+

binop = t.body[1].value.values[2].value

`

``

243

`+

self.assertEqual(type(binop), ast.BinOp)

`

``

244

`+

self.assertEqual(type(binop.left), ast.Name)

`

``

245

`+

self.assertEqual(type(binop.op), ast.Mult)

`

``

246

`+

self.assertEqual(type(binop.right), ast.Call)

`

``

247

`+

self.assertEqual(binop.lineno, 3)

`

``

248

`+

self.assertEqual(binop.left.lineno, 3)

`

``

249

`+

self.assertEqual(binop.right.lineno, 3)

`

``

250

`+

self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong

`

``

251

`+

self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong

`

``

252

`+

self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong

`

``

253

`+

check the third binop location

`

``

254

`+

binop = t.body[1].value.values[4].value

`

``

255

`+

self.assertEqual(type(binop), ast.BinOp)

`

``

256

`+

self.assertEqual(type(binop.left), ast.Name)

`

``

257

`+

self.assertEqual(type(binop.op), ast.Mult)

`

``

258

`+

self.assertEqual(type(binop.right), ast.Call)

`

``

259

`+

self.assertEqual(binop.lineno, 3)

`

``

260

`+

self.assertEqual(binop.left.lineno, 3)

`

``

261

`+

self.assertEqual(binop.right.lineno, 3)

`

``

262

`+

self.assertEqual(binop.col_offset, 3) # FIXME: this is wrong

`

``

263

`+

self.assertEqual(binop.left.col_offset, 3) # FIXME: this is wrong

`

``

264

`+

self.assertEqual(binop.right.col_offset, 7) # FIXME: this is wrong

`

``

265

+

``

266

`+

def test_ast_line_numbers_multiline_fstring(self):

`

``

267

`+

FIXME: This test demonstrates invalid behavior due to JoinedStr's

`

``

268

`+

immediate child nodes containing the wrong lineno. The enclosed

`

``

269

`+

expressions have valid line information and column offsets.

`

``

270

`+

See bpo-16806 and bpo-30465 for details.

`

``

271

`+

expr = """

`

``

272

`+

a = 10

`

``

273

`+

f'''

`

``

274

`+

{a

`

``

275

`+

`

``

276

`+

x()}

`

``

277

`+

non-important content

`

``

278

`+

'''

`

``

279

`+

"""

`

``

280

`+

t = ast.parse(expr)

`

``

281

`+

self.assertEqual(type(t), ast.Module)

`

``

282

`+

self.assertEqual(len(t.body), 2)

`

``

283

`` +

check a = 10

``

``

284

`+

self.assertEqual(type(t.body[0]), ast.Assign)

`

``

285

`+

self.assertEqual(t.body[0].lineno, 2)

`

``

286

`` +

check f'...'

``

``

287

`+

self.assertEqual(type(t.body[1]), ast.Expr)

`

``

288

`+

self.assertEqual(type(t.body[1].value), ast.JoinedStr)

`

``

289

`+

self.assertEqual(len(t.body[1].value.values), 3)

`

``

290

`+

self.assertEqual(type(t.body[1].value.values[0]), ast.Str)

`

``

291

`+

self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue)

`

``

292

`+

self.assertEqual(type(t.body[1].value.values[2]), ast.Str)

`

``

293

`+

NOTE: the following invalid behavior is described in bpo-16806.

`

``

294

`+

- line number should be the first line (3), not the last (8)

`

``

295

`+

- column offset should not be -1

`

``

296

`+

self.assertEqual(t.body[1].lineno, 8)

`

``

297

`+

self.assertEqual(t.body[1].value.lineno, 8)

`

``

298

`+

self.assertEqual(t.body[1].value.values[0].lineno, 8)

`

``

299

`+

self.assertEqual(t.body[1].value.values[1].lineno, 8)

`

``

300

`+

self.assertEqual(t.body[1].value.values[2].lineno, 8)

`

``

301

`+

self.assertEqual(t.body[1].col_offset, -1)

`

``

302

`+

self.assertEqual(t.body[1].value.col_offset, -1)

`

``

303

`+

self.assertEqual(t.body[1].value.values[0].col_offset, -1)

`

``

304

`+

self.assertEqual(t.body[1].value.values[1].col_offset, -1)

`

``

305

`+

self.assertEqual(t.body[1].value.values[2].col_offset, -1)

`

``

306

`+

NOTE: the following lineno information and col_offset is correct for

`

``

307

`+

expressions within FormattedValues.

`

``

308

`+

binop = t.body[1].value.values[1].value

`

``

309

`+

self.assertEqual(type(binop), ast.BinOp)

`

``

310

`+

self.assertEqual(type(binop.left), ast.Name)

`

``

311

`+

self.assertEqual(type(binop.op), ast.Mult)

`

``

312

`+

self.assertEqual(type(binop.right), ast.Call)

`

``

313

`+

self.assertEqual(binop.lineno, 4)

`

``

314

`+

self.assertEqual(binop.left.lineno, 4)

`

``

315

`+

self.assertEqual(binop.right.lineno, 6)

`

``

316

`+

self.assertEqual(binop.col_offset, 3)

`

``

317

`+

self.assertEqual(binop.left.col_offset, 3)

`

``

318

`+

self.assertEqual(binop.right.col_offset, 7)

`

``

319

+

73

320

`def test_docstring(self):

`

74

321

`def f():

`

75

322

`f'''Not a docstring'''

`

`@@ -786,5 +1033,6 @@ def test_backslash_char(self):

`

786

1033

`self.assertEqual(eval('f"\\n"'), '')

`

787

1034

`self.assertEqual(eval('f"\\r"'), '')

`

788

1035

``

``

1036

+

789

1037

`if name == 'main':

`

790

1038

`unittest.main()

`