Mercurial > hg
comparison contrib/testparseutil.py @ 42330:5364ba1f796f
py3: make contrib/testparseutil.py to work on str(unicodes)
contrib/check-code work on unicodes and call functions from testparseutil.py
which before this patch used to work on bytes.
This path removes that inconsistency and make testparseutil.py work on unicodes.
This makes test-check-code.t and test-contrib-check-code.t work on Python 3
again.
Differential Revision: https://phab.mercurial-scm.org/D6391
author | Pulkit Goyal <7895pulkit@gmail.com> |
---|---|
date | Fri, 17 May 2019 00:04:29 +0530 |
parents | 99b4c6d73a72 |
children | 37f38e1dea44 |
comparison
equal
deleted
inserted
replaced
42329:c7652f7440d9 | 42330:5364ba1f796f |
---|---|
52 if isinstance(s, builtins.str): | 52 if isinstance(s, builtins.str): |
53 return s | 53 return s |
54 return s.decode(u'latin-1') | 54 return s.decode(u'latin-1') |
55 | 55 |
56 def opentext(f): | 56 def opentext(f): |
57 return open(f, 'rb') | 57 return open(f, 'r') |
58 else: | 58 else: |
59 stdin = sys.stdin | 59 stdin = sys.stdin |
60 stdout = sys.stdout | 60 stdout = sys.stdout |
61 stderr = sys.stderr | 61 stderr = sys.stderr |
62 | 62 |
162 ... def __init__(self, desc, matchfunc): | 162 ... def __init__(self, desc, matchfunc): |
163 ... self.desc = desc | 163 ... self.desc = desc |
164 ... self.matchfunc = matchfunc | 164 ... self.matchfunc = matchfunc |
165 ... def startsat(self, line): | 165 ... def startsat(self, line): |
166 ... return self.matchfunc(line) | 166 ... return self.matchfunc(line) |
167 >>> ambig1 = ambigmatcher(b'ambiguous #1', | 167 >>> ambig1 = ambigmatcher('ambiguous #1', |
168 ... lambda l: l.startswith(b' $ cat ')) | 168 ... lambda l: l.startswith(' $ cat ')) |
169 >>> ambig2 = ambigmatcher(b'ambiguous #2', | 169 >>> ambig2 = ambigmatcher('ambiguous #2', |
170 ... lambda l: l.endswith(b'<< EOF\\n')) | 170 ... lambda l: l.endswith('<< EOF\\n')) |
171 >>> lines = [b' $ cat > foo.py << EOF\\n'] | 171 >>> lines = [' $ cat > foo.py << EOF\\n'] |
172 >>> errors = [] | 172 >>> errors = [] |
173 >>> matchers = [ambig1, ambig2] | 173 >>> matchers = [ambig1, ambig2] |
174 >>> list(t for t in embedded(b'<dummy>', lines, errors, matchers)) | 174 >>> list(t for t in embedded('<dummy>', lines, errors, matchers)) |
175 [] | 175 [] |
176 >>> b2s(errors) | 176 >>> b2s(errors) |
177 ['<dummy>:1: ambiguous line for "ambiguous #1", "ambiguous #2"'] | 177 ['<dummy>:1: ambiguous line for "ambiguous #1", "ambiguous #2"'] |
178 | 178 |
179 """ | 179 """ |
180 matcher = None | 180 matcher = None |
181 ctx = filename = code = startline = None # for pyflakes | 181 ctx = filename = code = startline = None # for pyflakes |
182 | 182 |
183 for lineno, line in enumerate(lines, 1): | 183 for lineno, line in enumerate(lines, 1): |
184 if not line.endswith(b'\n'): | 184 if not line.endswith('\n'): |
185 line += b'\n' # to normalize EOF line | 185 line += '\n' # to normalize EOF line |
186 if matcher: # now, inside embedded code | 186 if matcher: # now, inside embedded code |
187 if matcher.endsat(ctx, line): | 187 if matcher.endsat(ctx, line): |
188 codeatend = matcher.codeatend(ctx, line) | 188 codeatend = matcher.codeatend(ctx, line) |
189 if codeatend is not None: | 189 if codeatend is not None: |
190 code.append(codeatend) | 190 code.append(codeatend) |
191 if not matcher.ignores(ctx): | 191 if not matcher.ignores(ctx): |
192 yield (filename, startline, lineno, b''.join(code)) | 192 yield (filename, startline, lineno, ''.join(code)) |
193 matcher = None | 193 matcher = None |
194 # DO NOT "continue", because line might start next fragment | 194 # DO NOT "continue", because line might start next fragment |
195 elif not matcher.isinside(ctx, line): | 195 elif not matcher.isinside(ctx, line): |
196 # this is an error of basefile | 196 # this is an error of basefile |
197 # (if matchers are implemented correctly) | 197 # (if matchers are implemented correctly) |
198 errors.append(b'%s:%d: unexpected line for "%s"' | 198 errors.append('%s:%d: unexpected line for "%s"' |
199 % (basefile, lineno, matcher.desc)) | 199 % (basefile, lineno, matcher.desc)) |
200 # stop extracting embedded code by current 'matcher', | 200 # stop extracting embedded code by current 'matcher', |
201 # because appearance of unexpected line might mean | 201 # because appearance of unexpected line might mean |
202 # that expected end-of-embedded-code line might never | 202 # that expected end-of-embedded-code line might never |
203 # appear | 203 # appear |
216 if ctx: | 216 if ctx: |
217 matched.append((m, ctx)) | 217 matched.append((m, ctx)) |
218 if matched: | 218 if matched: |
219 if len(matched) > 1: | 219 if len(matched) > 1: |
220 # this is an error of matchers, maybe | 220 # this is an error of matchers, maybe |
221 errors.append(b'%s:%d: ambiguous line for %s' % | 221 errors.append('%s:%d: ambiguous line for %s' % |
222 (basefile, lineno, | 222 (basefile, lineno, |
223 b', '.join([b'"%s"' % m.desc | 223 ', '.join(['"%s"' % m.desc |
224 for m, c in matched]))) | 224 for m, c in matched]))) |
225 # omit extracting embedded code, because choosing | 225 # omit extracting embedded code, because choosing |
226 # arbitrary matcher from matched ones might fail to | 226 # arbitrary matcher from matched ones might fail to |
227 # detect the end of embedded code as expected. | 227 # detect the end of embedded code as expected. |
228 continue | 228 continue |
237 startline = lineno + 1 | 237 startline = lineno + 1 |
238 | 238 |
239 if matcher: | 239 if matcher: |
240 # examine whether EOF ends embedded code, because embedded | 240 # examine whether EOF ends embedded code, because embedded |
241 # code isn't yet ended explicitly | 241 # code isn't yet ended explicitly |
242 if matcher.endsat(ctx, b'\n'): | 242 if matcher.endsat(ctx, '\n'): |
243 codeatend = matcher.codeatend(ctx, b'\n') | 243 codeatend = matcher.codeatend(ctx, '\n') |
244 if codeatend is not None: | 244 if codeatend is not None: |
245 code.append(codeatend) | 245 code.append(codeatend) |
246 if not matcher.ignores(ctx): | 246 if not matcher.ignores(ctx): |
247 yield (filename, startline, lineno + 1, b''.join(code)) | 247 yield (filename, startline, lineno + 1, ''.join(code)) |
248 else: | 248 else: |
249 # this is an error of basefile | 249 # this is an error of basefile |
250 # (if matchers are implemented correctly) | 250 # (if matchers are implemented correctly) |
251 errors.append(b'%s:%d: unexpected end of file for "%s"' | 251 errors.append('%s:%d: unexpected end of file for "%s"' |
252 % (basefile, lineno, matcher.desc)) | 252 % (basefile, lineno, matcher.desc)) |
253 | 253 |
254 # heredoc limit mark to ignore embedded code at check-code.py or so | 254 # heredoc limit mark to ignore embedded code at check-code.py or so |
255 heredocignorelimit = b'NO_CHECK_EOF' | 255 heredocignorelimit = 'NO_CHECK_EOF' |
256 | 256 |
257 # the pattern to match against cases below, and to return a limit mark | 257 # the pattern to match against cases below, and to return a limit mark |
258 # string as 'lname' group | 258 # string as 'lname' group |
259 # | 259 # |
260 # - << LIMITMARK | 260 # - << LIMITMARK |
261 # - << "LIMITMARK" | 261 # - << "LIMITMARK" |
262 # - << 'LIMITMARK' | 262 # - << 'LIMITMARK' |
263 heredoclimitpat = br'\s*<<\s*(?P<lquote>["\']?)(?P<limit>\w+)(?P=lquote)' | 263 heredoclimitpat = r'\s*<<\s*(?P<lquote>["\']?)(?P<limit>\w+)(?P=lquote)' |
264 | 264 |
265 class fileheredocmatcher(embeddedmatcher): | 265 class fileheredocmatcher(embeddedmatcher): |
266 """Detect "cat > FILE << LIMIT" style embedded code | 266 """Detect "cat > FILE << LIMIT" style embedded code |
267 | 267 |
268 >>> matcher = fileheredocmatcher(b'heredoc .py file', br'[^<]+\\.py') | 268 >>> matcher = fileheredocmatcher(b'heredoc .py file', br'[^<]+\\.py') |
269 >>> b2s(matcher.startsat(b' $ cat > file.py << EOF\\n')) | 269 >>> b2s(matcher.startsat(' $ cat > file.py << EOF\\n')) |
270 ('file.py', ' > EOF\\n') | 270 ('file.py', ' > EOF\\n') |
271 >>> b2s(matcher.startsat(b' $ cat >>file.py <<EOF\\n')) | 271 >>> b2s(matcher.startsat(' $ cat >>file.py <<EOF\\n')) |
272 ('file.py', ' > EOF\\n') | 272 ('file.py', ' > EOF\\n') |
273 >>> b2s(matcher.startsat(b' $ cat> \\x27any file.py\\x27<< "EOF"\\n')) | 273 >>> b2s(matcher.startsat(' $ cat> \\x27any file.py\\x27<< "EOF"\\n')) |
274 ('any file.py', ' > EOF\\n') | 274 ('any file.py', ' > EOF\\n') |
275 >>> b2s(matcher.startsat(b" $ cat > file.py << 'ANYLIMIT'\\n")) | 275 >>> b2s(matcher.startsat(" $ cat > file.py << 'ANYLIMIT'\\n")) |
276 ('file.py', ' > ANYLIMIT\\n') | 276 ('file.py', ' > ANYLIMIT\\n') |
277 >>> b2s(matcher.startsat(b' $ cat<<ANYLIMIT>"file.py"\\n')) | 277 >>> b2s(matcher.startsat(' $ cat<<ANYLIMIT>"file.py"\\n')) |
278 ('file.py', ' > ANYLIMIT\\n') | 278 ('file.py', ' > ANYLIMIT\\n') |
279 >>> start = b' $ cat > file.py << EOF\\n' | 279 >>> start = ' $ cat > file.py << EOF\\n' |
280 >>> ctx = matcher.startsat(start) | 280 >>> ctx = matcher.startsat(start) |
281 >>> matcher.codeatstart(ctx, start) | 281 >>> matcher.codeatstart(ctx, start) |
282 >>> b2s(matcher.filename(ctx)) | 282 >>> b2s(matcher.filename(ctx)) |
283 'file.py' | 283 'file.py' |
284 >>> matcher.ignores(ctx) | 284 >>> matcher.ignores(ctx) |
285 False | 285 False |
286 >>> inside = b' > foo = 1\\n' | 286 >>> inside = ' > foo = 1\\n' |
287 >>> matcher.endsat(ctx, inside) | 287 >>> matcher.endsat(ctx, inside) |
288 False | 288 False |
289 >>> matcher.isinside(ctx, inside) | 289 >>> matcher.isinside(ctx, inside) |
290 True | 290 True |
291 >>> b2s(matcher.codeinside(ctx, inside)) | 291 >>> b2s(matcher.codeinside(ctx, inside)) |
292 'foo = 1\\n' | 292 'foo = 1\\n' |
293 >>> end = b' > EOF\\n' | 293 >>> end = ' > EOF\\n' |
294 >>> matcher.endsat(ctx, end) | 294 >>> matcher.endsat(ctx, end) |
295 True | 295 True |
296 >>> matcher.codeatend(ctx, end) | 296 >>> matcher.codeatend(ctx, end) |
297 >>> matcher.endsat(ctx, b' > EOFEOF\\n') | 297 >>> matcher.endsat(ctx, ' > EOFEOF\\n') |
298 False | 298 False |
299 >>> ctx = matcher.startsat(b' $ cat > file.py << NO_CHECK_EOF\\n') | 299 >>> ctx = matcher.startsat(' $ cat > file.py << NO_CHECK_EOF\\n') |
300 >>> matcher.ignores(ctx) | 300 >>> matcher.ignores(ctx) |
301 True | 301 True |
302 """ | 302 """ |
303 _prefix = b' > ' | 303 _prefix = ' > ' |
304 | 304 |
305 def __init__(self, desc, namepat): | 305 def __init__(self, desc, namepat): |
306 super(fileheredocmatcher, self).__init__(desc) | 306 super(fileheredocmatcher, self).__init__(desc) |
307 | 307 |
308 # build the pattern to match against cases below (and ">>" | 308 # build the pattern to match against cases below (and ">>" |
310 # group | 310 # group |
311 # | 311 # |
312 # - > NAMEPAT | 312 # - > NAMEPAT |
313 # - > "NAMEPAT" | 313 # - > "NAMEPAT" |
314 # - > 'NAMEPAT' | 314 # - > 'NAMEPAT' |
315 namepat = (br'\s*>>?\s*(?P<nquote>["\']?)(?P<name>%s)(?P=nquote)' | 315 namepat = (r'\s*>>?\s*(?P<nquote>["\']?)(?P<name>%s)(?P=nquote)' |
316 % namepat) | 316 % namepat) |
317 self._fileres = [ | 317 self._fileres = [ |
318 # "cat > NAME << LIMIT" case | 318 # "cat > NAME << LIMIT" case |
319 re.compile(br' \$ \s*cat' + namepat + heredoclimitpat), | 319 re.compile(r' \$ \s*cat' + namepat + heredoclimitpat), |
320 # "cat << LIMIT > NAME" case | 320 # "cat << LIMIT > NAME" case |
321 re.compile(br' \$ \s*cat' + heredoclimitpat + namepat), | 321 re.compile(r' \$ \s*cat' + heredoclimitpat + namepat), |
322 ] | 322 ] |
323 | 323 |
324 def startsat(self, line): | 324 def startsat(self, line): |
325 # ctx is (filename, END-LINE-OF-EMBEDDED-CODE) tuple | 325 # ctx is (filename, END-LINE-OF-EMBEDDED-CODE) tuple |
326 for filere in self._fileres: | 326 for filere in self._fileres: |
327 matched = filere.match(line) | 327 matched = filere.match(line) |
328 if matched: | 328 if matched: |
329 return (matched.group('name'), | 329 return (matched.group('name'), |
330 b' > %s\n' % matched.group('limit')) | 330 ' > %s\n' % matched.group('limit')) |
331 | 331 |
332 def endsat(self, ctx, line): | 332 def endsat(self, ctx, line): |
333 return ctx[1] == line | 333 return ctx[1] == line |
334 | 334 |
335 def isinside(self, ctx, line): | 335 def isinside(self, ctx, line): |
336 return line.startswith(self._prefix) | 336 return line.startswith(self._prefix) |
337 | 337 |
338 def ignores(self, ctx): | 338 def ignores(self, ctx): |
339 return b' > %s\n' % heredocignorelimit == ctx[1] | 339 return ' > %s\n' % heredocignorelimit == ctx[1] |
340 | 340 |
341 def filename(self, ctx): | 341 def filename(self, ctx): |
342 return ctx[0] | 342 return ctx[0] |
343 | 343 |
344 def codeatstart(self, ctx, line): | 344 def codeatstart(self, ctx, line): |
355 | 355 |
356 class pydoctestmatcher(embeddedmatcher): | 356 class pydoctestmatcher(embeddedmatcher): |
357 """Detect ">>> code" style embedded python code | 357 """Detect ">>> code" style embedded python code |
358 | 358 |
359 >>> matcher = pydoctestmatcher() | 359 >>> matcher = pydoctestmatcher() |
360 >>> startline = b' >>> foo = 1\\n' | 360 >>> startline = ' >>> foo = 1\\n' |
361 >>> matcher.startsat(startline) | 361 >>> matcher.startsat(startline) |
362 True | 362 True |
363 >>> matcher.startsat(b' ... foo = 1\\n') | 363 >>> matcher.startsat(' ... foo = 1\\n') |
364 False | 364 False |
365 >>> ctx = matcher.startsat(startline) | 365 >>> ctx = matcher.startsat(startline) |
366 >>> matcher.filename(ctx) | 366 >>> matcher.filename(ctx) |
367 >>> matcher.ignores(ctx) | 367 >>> matcher.ignores(ctx) |
368 False | 368 False |
369 >>> b2s(matcher.codeatstart(ctx, startline)) | 369 >>> b2s(matcher.codeatstart(ctx, startline)) |
370 'foo = 1\\n' | 370 'foo = 1\\n' |
371 >>> inside = b' >>> foo = 1\\n' | 371 >>> inside = ' >>> foo = 1\\n' |
372 >>> matcher.endsat(ctx, inside) | 372 >>> matcher.endsat(ctx, inside) |
373 False | 373 False |
374 >>> matcher.isinside(ctx, inside) | 374 >>> matcher.isinside(ctx, inside) |
375 True | 375 True |
376 >>> b2s(matcher.codeinside(ctx, inside)) | 376 >>> b2s(matcher.codeinside(ctx, inside)) |
377 'foo = 1\\n' | 377 'foo = 1\\n' |
378 >>> inside = b' ... foo = 1\\n' | 378 >>> inside = ' ... foo = 1\\n' |
379 >>> matcher.endsat(ctx, inside) | 379 >>> matcher.endsat(ctx, inside) |
380 False | 380 False |
381 >>> matcher.isinside(ctx, inside) | 381 >>> matcher.isinside(ctx, inside) |
382 True | 382 True |
383 >>> b2s(matcher.codeinside(ctx, inside)) | 383 >>> b2s(matcher.codeinside(ctx, inside)) |
384 'foo = 1\\n' | 384 'foo = 1\\n' |
385 >>> inside = b' expected output\\n' | 385 >>> inside = ' expected output\\n' |
386 >>> matcher.endsat(ctx, inside) | 386 >>> matcher.endsat(ctx, inside) |
387 False | 387 False |
388 >>> matcher.isinside(ctx, inside) | 388 >>> matcher.isinside(ctx, inside) |
389 True | 389 True |
390 >>> b2s(matcher.codeinside(ctx, inside)) | 390 >>> b2s(matcher.codeinside(ctx, inside)) |
391 '\\n' | 391 '\\n' |
392 >>> inside = b' \\n' | 392 >>> inside = ' \\n' |
393 >>> matcher.endsat(ctx, inside) | 393 >>> matcher.endsat(ctx, inside) |
394 False | 394 False |
395 >>> matcher.isinside(ctx, inside) | 395 >>> matcher.isinside(ctx, inside) |
396 True | 396 True |
397 >>> b2s(matcher.codeinside(ctx, inside)) | 397 >>> b2s(matcher.codeinside(ctx, inside)) |
398 '\\n' | 398 '\\n' |
399 >>> end = b' $ foo bar\\n' | 399 >>> end = ' $ foo bar\\n' |
400 >>> matcher.endsat(ctx, end) | 400 >>> matcher.endsat(ctx, end) |
401 True | 401 True |
402 >>> matcher.codeatend(ctx, end) | 402 >>> matcher.codeatend(ctx, end) |
403 >>> end = b'\\n' | 403 >>> end = '\\n' |
404 >>> matcher.endsat(ctx, end) | 404 >>> matcher.endsat(ctx, end) |
405 True | 405 True |
406 >>> matcher.codeatend(ctx, end) | 406 >>> matcher.codeatend(ctx, end) |
407 """ | 407 """ |
408 _prefix = b' >>> ' | 408 _prefix = ' >>> ' |
409 _prefixre = re.compile(br' (>>>|\.\.\.) ') | 409 _prefixre = re.compile(r' (>>>|\.\.\.) ') |
410 | 410 |
411 # If a line matches against not _prefixre but _outputre, that line | 411 # If a line matches against not _prefixre but _outputre, that line |
412 # is "an expected output line" (= not a part of code fragment). | 412 # is "an expected output line" (= not a part of code fragment). |
413 # | 413 # |
414 # Strictly speaking, a line matching against "(#if|#else|#endif)" | 414 # Strictly speaking, a line matching against "(#if|#else|#endif)" |
415 # is also treated similarly in "inline python code" semantics by | 415 # is also treated similarly in "inline python code" semantics by |
416 # run-tests.py. But "directive line inside inline python code" | 416 # run-tests.py. But "directive line inside inline python code" |
417 # should be rejected by Mercurial reviewers. Therefore, this | 417 # should be rejected by Mercurial reviewers. Therefore, this |
418 # regexp does not matche against such directive lines. | 418 # regexp does not matche against such directive lines. |
419 _outputre = re.compile(br' $| [^$]') | 419 _outputre = re.compile(r' $| [^$]') |
420 | 420 |
421 def __init__(self): | 421 def __init__(self): |
422 super(pydoctestmatcher, self).__init__(b"doctest style python code") | 422 super(pydoctestmatcher, self).__init__("doctest style python code") |
423 | 423 |
424 def startsat(self, line): | 424 def startsat(self, line): |
425 # ctx is "True" | 425 # ctx is "True" |
426 return line.startswith(self._prefix) | 426 return line.startswith(self._prefix) |
427 | 427 |
444 return None # no embedded code at end line | 444 return None # no embedded code at end line |
445 | 445 |
446 def codeinside(self, ctx, line): | 446 def codeinside(self, ctx, line): |
447 if self._prefixre.match(line): | 447 if self._prefixre.match(line): |
448 return line[len(self._prefix):] # strip prefix ' >>> '/' ... ' | 448 return line[len(self._prefix):] # strip prefix ' >>> '/' ... ' |
449 return b'\n' # an expected output line is treated as an empty line | 449 return '\n' # an expected output line is treated as an empty line |
450 | 450 |
451 class pyheredocmatcher(embeddedmatcher): | 451 class pyheredocmatcher(embeddedmatcher): |
452 """Detect "python << LIMIT" style embedded python code | 452 """Detect "python << LIMIT" style embedded python code |
453 | 453 |
454 >>> matcher = pyheredocmatcher() | 454 >>> matcher = pyheredocmatcher() |
455 >>> b2s(matcher.startsat(b' $ python << EOF\\n')) | 455 >>> b2s(matcher.startsat(' $ python << EOF\\n')) |
456 ' > EOF\\n' | 456 ' > EOF\\n' |
457 >>> b2s(matcher.startsat(b' $ $PYTHON <<EOF\\n')) | 457 >>> b2s(matcher.startsat(' $ $PYTHON <<EOF\\n')) |
458 ' > EOF\\n' | 458 ' > EOF\\n' |
459 >>> b2s(matcher.startsat(b' $ "$PYTHON"<< "EOF"\\n')) | 459 >>> b2s(matcher.startsat(' $ "$PYTHON"<< "EOF"\\n')) |
460 ' > EOF\\n' | 460 ' > EOF\\n' |
461 >>> b2s(matcher.startsat(b" $ $PYTHON << 'ANYLIMIT'\\n")) | 461 >>> b2s(matcher.startsat(" $ $PYTHON << 'ANYLIMIT'\\n")) |
462 ' > ANYLIMIT\\n' | 462 ' > ANYLIMIT\\n' |
463 >>> matcher.startsat(b' $ "$PYTHON" < EOF\\n') | 463 >>> matcher.startsat(' $ "$PYTHON" < EOF\\n') |
464 >>> start = b' $ python << EOF\\n' | 464 >>> start = ' $ python << EOF\\n' |
465 >>> ctx = matcher.startsat(start) | 465 >>> ctx = matcher.startsat(start) |
466 >>> matcher.codeatstart(ctx, start) | 466 >>> matcher.codeatstart(ctx, start) |
467 >>> matcher.filename(ctx) | 467 >>> matcher.filename(ctx) |
468 >>> matcher.ignores(ctx) | 468 >>> matcher.ignores(ctx) |
469 False | 469 False |
470 >>> inside = b' > foo = 1\\n' | 470 >>> inside = ' > foo = 1\\n' |
471 >>> matcher.endsat(ctx, inside) | 471 >>> matcher.endsat(ctx, inside) |
472 False | 472 False |
473 >>> matcher.isinside(ctx, inside) | 473 >>> matcher.isinside(ctx, inside) |
474 True | 474 True |
475 >>> b2s(matcher.codeinside(ctx, inside)) | 475 >>> b2s(matcher.codeinside(ctx, inside)) |
476 'foo = 1\\n' | 476 'foo = 1\\n' |
477 >>> end = b' > EOF\\n' | 477 >>> end = ' > EOF\\n' |
478 >>> matcher.endsat(ctx, end) | 478 >>> matcher.endsat(ctx, end) |
479 True | 479 True |
480 >>> matcher.codeatend(ctx, end) | 480 >>> matcher.codeatend(ctx, end) |
481 >>> matcher.endsat(ctx, b' > EOFEOF\\n') | 481 >>> matcher.endsat(ctx, ' > EOFEOF\\n') |
482 False | 482 False |
483 >>> ctx = matcher.startsat(b' $ python << NO_CHECK_EOF\\n') | 483 >>> ctx = matcher.startsat(' $ python << NO_CHECK_EOF\\n') |
484 >>> matcher.ignores(ctx) | 484 >>> matcher.ignores(ctx) |
485 True | 485 True |
486 """ | 486 """ |
487 _prefix = b' > ' | 487 _prefix = ' > ' |
488 | 488 |
489 _startre = re.compile(br' \$ (\$PYTHON|"\$PYTHON"|python).*' + | 489 _startre = re.compile(r' \$ (\$PYTHON|"\$PYTHON"|python).*' + |
490 heredoclimitpat) | 490 heredoclimitpat) |
491 | 491 |
492 def __init__(self): | 492 def __init__(self): |
493 super(pyheredocmatcher, self).__init__(b"heredoc python invocation") | 493 super(pyheredocmatcher, self).__init__("heredoc python invocation") |
494 | 494 |
495 def startsat(self, line): | 495 def startsat(self, line): |
496 # ctx is END-LINE-OF-EMBEDDED-CODE | 496 # ctx is END-LINE-OF-EMBEDDED-CODE |
497 matched = self._startre.match(line) | 497 matched = self._startre.match(line) |
498 if matched: | 498 if matched: |
499 return b' > %s\n' % matched.group('limit') | 499 return ' > %s\n' % matched.group('limit') |
500 | 500 |
501 def endsat(self, ctx, line): | 501 def endsat(self, ctx, line): |
502 return ctx == line | 502 return ctx == line |
503 | 503 |
504 def isinside(self, ctx, line): | 504 def isinside(self, ctx, line): |
505 return line.startswith(self._prefix) | 505 return line.startswith(self._prefix) |
506 | 506 |
507 def ignores(self, ctx): | 507 def ignores(self, ctx): |
508 return b' > %s\n' % heredocignorelimit == ctx | 508 return ' > %s\n' % heredocignorelimit == ctx |
509 | 509 |
510 def filename(self, ctx): | 510 def filename(self, ctx): |
511 return None # no filename | 511 return None # no filename |
512 | 512 |
513 def codeatstart(self, ctx, line): | 513 def codeatstart(self, ctx, line): |
522 _pymatchers = [ | 522 _pymatchers = [ |
523 pydoctestmatcher(), | 523 pydoctestmatcher(), |
524 pyheredocmatcher(), | 524 pyheredocmatcher(), |
525 # use '[^<]+' instead of '\S+', in order to match against | 525 # use '[^<]+' instead of '\S+', in order to match against |
526 # paths including whitespaces | 526 # paths including whitespaces |
527 fileheredocmatcher(b'heredoc .py file', br'[^<]+\.py'), | 527 fileheredocmatcher('heredoc .py file', r'[^<]+\.py'), |
528 ] | 528 ] |
529 | 529 |
530 def pyembedded(basefile, lines, errors): | 530 def pyembedded(basefile, lines, errors): |
531 return embedded(basefile, lines, errors, _pymatchers) | 531 return embedded(basefile, lines, errors, _pymatchers) |
532 | 532 |
534 # for embedded shell script | 534 # for embedded shell script |
535 | 535 |
536 _shmatchers = [ | 536 _shmatchers = [ |
537 # use '[^<]+' instead of '\S+', in order to match against | 537 # use '[^<]+' instead of '\S+', in order to match against |
538 # paths including whitespaces | 538 # paths including whitespaces |
539 fileheredocmatcher(b'heredoc .sh file', br'[^<]+\.sh'), | 539 fileheredocmatcher('heredoc .sh file', r'[^<]+\.sh'), |
540 ] | 540 ] |
541 | 541 |
542 def shembedded(basefile, lines, errors): | 542 def shembedded(basefile, lines, errors): |
543 return embedded(basefile, lines, errors, _shmatchers) | 543 return embedded(basefile, lines, errors, _shmatchers) |
544 | 544 |
546 # for embedded hgrc configuration | 546 # for embedded hgrc configuration |
547 | 547 |
548 _hgrcmatchers = [ | 548 _hgrcmatchers = [ |
549 # use '[^<]+' instead of '\S+', in order to match against | 549 # use '[^<]+' instead of '\S+', in order to match against |
550 # paths including whitespaces | 550 # paths including whitespaces |
551 fileheredocmatcher(b'heredoc hgrc file', | 551 fileheredocmatcher('heredoc hgrc file', |
552 br'(([^/<]+/)+hgrc|\$HGRCPATH|\${HGRCPATH})'), | 552 r'(([^/<]+/)+hgrc|\$HGRCPATH|\${HGRCPATH})'), |
553 ] | 553 ] |
554 | 554 |
555 def hgrcembedded(basefile, lines, errors): | 555 def hgrcembedded(basefile, lines, errors): |
556 return embedded(basefile, lines, errors, _hgrcmatchers) | 556 return embedded(basefile, lines, errors, _hgrcmatchers) |
557 | 557 |
563 | 563 |
564 def showembedded(basefile, lines, embeddedfunc, opts): | 564 def showembedded(basefile, lines, embeddedfunc, opts): |
565 errors = [] | 565 errors = [] |
566 for name, starts, ends, code in embeddedfunc(basefile, lines, errors): | 566 for name, starts, ends, code in embeddedfunc(basefile, lines, errors): |
567 if not name: | 567 if not name: |
568 name = b'<anonymous>' | 568 name = '<anonymous>' |
569 writeout(b"%s:%d: %s starts\n" % (basefile, starts, name)) | 569 writeout("%s:%d: %s starts\n" % (basefile, starts, name)) |
570 if opts.verbose and code: | 570 if opts.verbose and code: |
571 writeout(b" |%s\n" % | 571 writeout(" |%s\n" % |
572 b"\n |".join(l for l in code.splitlines())) | 572 "\n |".join(l for l in code.splitlines())) |
573 writeout(b"%s:%d: %s ends\n" % (basefile, ends, name)) | 573 writeout("%s:%d: %s ends\n" % (basefile, ends, name)) |
574 for e in errors: | 574 for e in errors: |
575 writeerr(b"%s\n" % e) | 575 writeerr("%s\n" % e) |
576 return len(errors) | 576 return len(errors) |
577 | 577 |
578 def applyembedded(args, embeddedfunc, opts): | 578 def applyembedded(args, embeddedfunc, opts): |
579 ret = 0 | 579 ret = 0 |
580 if args: | 580 if args: |
581 for f in args: | 581 for f in args: |
582 with opentext(f) as fp: | 582 with opentext(f) as fp: |
583 if showembedded(bytestr(f), fp, embeddedfunc, opts): | 583 if showembedded(f, fp, embeddedfunc, opts): |
584 ret = 1 | 584 ret = 1 |
585 else: | 585 else: |
586 lines = [l for l in stdin.readlines()] | 586 lines = [l for l in stdin.readlines()] |
587 if showembedded(b'<stdin>', lines, embeddedfunc, opts): | 587 if showembedded('<stdin>', lines, embeddedfunc, opts): |
588 ret = 1 | 588 ret = 1 |
589 return ret | 589 return ret |
590 | 590 |
591 commands = {} | 591 commands = {} |
592 def command(name, desc): | 592 def command(name, desc): |