github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-tools-src/grumpy_tools/compiler/stmt_test.py (about) 1 # coding=utf-8 2 3 # Copyright 2016 Google Inc. All Rights Reserved. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 """Tests for StatementVisitor.""" 18 19 from __future__ import unicode_literals 20 21 import re 22 import subprocess 23 import textwrap 24 import unittest 25 26 from grumpy_tools.compiler import block 27 from grumpy_tools.compiler import imputil 28 from grumpy_tools.compiler import shard_test 29 from grumpy_tools.compiler import stmt 30 from grumpy_tools.compiler import util 31 import pythonparser 32 from pythonparser import ast 33 34 import pytest 35 36 class StatementVisitorTest(unittest.TestCase): 37 38 def testAssertNoMsg(self): 39 self.assertEqual((0, 'AssertionError()\n'), _GrumpRun(textwrap.dedent("""\ 40 try: 41 assert False 42 except AssertionError as e: 43 print repr(e)"""))) 44 45 def testAssertMsg(self): 46 want = (0, "AssertionError('foo',)\n") 47 self.assertEqual(want, _GrumpRun(textwrap.dedent("""\ 48 try: 49 assert False, 'foo' 50 except AssertionError as e: 51 print repr(e)"""))) 52 53 def testBareAssert(self): 54 # Assertion errors at the top level of a block should raise: 55 # https://github.com/google/grumpy/issues/18 56 want = (0, 'ok\n') 57 self.assertEqual(want, _GrumpRun(textwrap.dedent("""\ 58 def foo(): 59 assert False 60 try: 61 foo() 62 except AssertionError: 63 print 'ok' 64 else: 65 print 'bad'"""))) 66 67 def testAssignAttribute(self): 68 self.assertEqual((0, '123\n'), _GrumpRun(textwrap.dedent("""\ 69 e = Exception() 70 e.foo = 123 71 print e.foo"""))) 72 73 def testAssignName(self): 74 self.assertEqual((0, 'bar\n'), _GrumpRun(textwrap.dedent("""\ 75 foo = 'bar' 76 print foo"""))) 77 78 def testAssignMultiple(self): 79 self.assertEqual((0, 'baz baz\n'), _GrumpRun(textwrap.dedent("""\ 80 foo = bar = 'baz' 81 print foo, bar"""))) 82 83 def testAssignSubscript(self): 84 self.assertEqual((0, "{'bar': None}\n"), _GrumpRun(textwrap.dedent("""\ 85 foo = {} 86 foo['bar'] = None 87 print foo"""))) 88 89 def testAssignTuple(self): 90 self.assertEqual((0, 'a b\n'), _GrumpRun(textwrap.dedent("""\ 91 baz = ('a', 'b') 92 foo, bar = baz 93 print foo, bar"""))) 94 95 def testAugAssign(self): 96 self.assertEqual((0, '42\n'), _GrumpRun(textwrap.dedent("""\ 97 foo = 41 98 foo += 1 99 print foo"""))) 100 101 def testAugAssignBitAnd(self): 102 self.assertEqual((0, '3\n'), _GrumpRun(textwrap.dedent("""\ 103 foo = 7 104 foo &= 3 105 print foo"""))) 106 107 def testAugAssignPow(self): 108 self.assertEqual((0, '64\n'), _GrumpRun(textwrap.dedent("""\ 109 foo = 8 110 foo **= 2 111 print foo"""))) 112 113 def testClassDef(self): 114 self.assertEqual((0, "<type 'type'>\n"), _GrumpRun(textwrap.dedent("""\ 115 class Foo(object): 116 pass 117 print type(Foo)"""))) 118 119 def testClassDefWithVar(self): 120 self.assertEqual((0, 'abc\n'), _GrumpRun(textwrap.dedent("""\ 121 class Foo(object): 122 bar = 'abc' 123 print Foo.bar"""))) 124 125 def testDeleteAttribute(self): 126 self.assertEqual((0, 'False\n'), _GrumpRun(textwrap.dedent("""\ 127 class Foo(object): 128 bar = 42 129 del Foo.bar 130 print hasattr(Foo, 'bar')"""))) 131 132 def testDeleteClassLocal(self): 133 self.assertEqual((0, 'False\n'), _GrumpRun(textwrap.dedent("""\ 134 class Foo(object): 135 bar = 'baz' 136 del bar 137 print hasattr(Foo, 'bar')"""))) 138 139 def testDeleteGlobal(self): 140 self.assertEqual((0, 'False\n'), _GrumpRun(textwrap.dedent("""\ 141 foo = 42 142 del foo 143 print 'foo' in globals()"""))) 144 145 def testDeleteLocal(self): 146 self.assertEqual((0, 'ok\n'), _GrumpRun(textwrap.dedent("""\ 147 def foo(): 148 bar = 123 149 del bar 150 try: 151 print bar 152 raise AssertionError 153 except UnboundLocalError: 154 print 'ok' 155 foo()"""))) 156 157 def testDeleteNonexistentLocal(self): 158 self.assertRaisesRegexp( 159 util.ParseError, 'cannot delete nonexistent local', 160 _ParseAndVisit, 'def foo():\n del bar') 161 162 def testDeleteSubscript(self): 163 self.assertEqual((0, '{}\n'), _GrumpRun(textwrap.dedent("""\ 164 foo = {'bar': 'baz'} 165 del foo['bar'] 166 print foo"""))) 167 168 def testExprCall(self): 169 self.assertEqual((0, 'bar\n'), _GrumpRun(textwrap.dedent("""\ 170 def foo(): 171 print 'bar' 172 foo()"""))) 173 174 def testExprNameGlobal(self): 175 self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\ 176 foo = 42 177 foo"""))) 178 179 def testExprNameLocal(self): 180 self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\ 181 foo = 42 182 def bar(): 183 foo 184 bar()"""))) 185 186 def testFor(self): 187 self.assertEqual((0, '1\n2\n3\n'), _GrumpRun(textwrap.dedent("""\ 188 for i in (1, 2, 3): 189 print i"""))) 190 191 def testForBreak(self): 192 self.assertEqual((0, '1\n'), _GrumpRun(textwrap.dedent("""\ 193 for i in (1, 2, 3): 194 print i 195 break"""))) 196 197 def testForContinue(self): 198 self.assertEqual((0, '1\n2\n3\n'), _GrumpRun(textwrap.dedent("""\ 199 for i in (1, 2, 3): 200 print i 201 continue 202 raise AssertionError"""))) 203 204 def testForElse(self): 205 self.assertEqual((0, 'foo\nbar\n'), _GrumpRun(textwrap.dedent("""\ 206 for i in (1,): 207 print 'foo' 208 else: 209 print 'bar'"""))) 210 211 def testForElseBreakNotNested(self): 212 self.assertRaisesRegexp( 213 util.ParseError, "'continue' not in loop", 214 _ParseAndVisit, 'for i in (1,):\n pass\nelse:\n continue') 215 216 def testForElseContinueNotNested(self): 217 self.assertRaisesRegexp( 218 util.ParseError, "'continue' not in loop", 219 _ParseAndVisit, 'for i in (1,):\n pass\nelse:\n continue') 220 221 def testFunctionDecorator(self): 222 self.assertEqual((0, '<b>foo</b>\n'), _GrumpRun(textwrap.dedent("""\ 223 def bold(fn): 224 return lambda: '<b>' + fn() + '</b>' 225 @bold 226 def foo(): 227 return 'foo' 228 print foo()"""))) 229 230 def testFunctionDecoratorWithArg(self): 231 self.assertEqual((0, '<b id=red>foo</b>\n'), _GrumpRun(textwrap.dedent("""\ 232 def tag(name): 233 def bold(fn): 234 return lambda: '<b id=' + name + '>' + fn() + '</b>' 235 return bold 236 @tag('red') 237 def foo(): 238 return 'foo' 239 print foo()"""))) 240 241 def testFunctionDef(self): 242 self.assertEqual((0, 'bar baz\n'), _GrumpRun(textwrap.dedent("""\ 243 def foo(a, b): 244 print a, b 245 foo('bar', 'baz')"""))) 246 247 def testFunctionDefWithTupleArgs(self): 248 self.assertEqual((0, "('bar', 'baz')\n"), _GrumpRun(textwrap.dedent("""\ 249 def foo((a, b)): 250 print(a, b) 251 foo(('bar', 'baz'))"""))) 252 253 def testFunctionDefWithNestedTupleArgs(self): 254 self.assertEqual((0, "('bar', 'baz', 'qux')\n"), _GrumpRun(textwrap.dedent("""\ 255 def foo(((a, b), c)): 256 print(a, b, c) 257 foo((('bar', 'baz'), 'qux'))"""))) 258 259 def testFunctionDefWithMultipleTupleArgs(self): 260 self.assertEqual((0, "('bar', 'baz')\n"), _GrumpRun(textwrap.dedent("""\ 261 def foo(((a, ), (b, ))): 262 print(a, b) 263 foo((('bar',), ('baz', )))"""))) 264 265 def testFunctionDefTupleArgsInLambda(self): 266 self.assertEqual((0, "[(3, 2), (4, 3), (12, 1)]\n"), _GrumpRun(textwrap.dedent("""\ 267 c = {12: 1, 3: 2, 4: 3} 268 top = sorted(c.items(), key=lambda (k,v): v) 269 print (top)"""))) 270 271 def testFunctionDefGenerator(self): 272 self.assertEqual((0, "['foo', 'bar']\n"), _GrumpRun(textwrap.dedent("""\ 273 def gen(): 274 yield 'foo' 275 yield 'bar' 276 print list(gen())"""))) 277 278 def testFunctionDefGeneratorReturnValue(self): 279 self.assertRaisesRegexp( 280 util.ParseError, 'returning a value in a generator function', 281 _ParseAndVisit, 'def foo():\n yield 1\n return 2') 282 283 def testFunctionDefLocal(self): 284 self.assertEqual((0, 'baz\n'), _GrumpRun(textwrap.dedent("""\ 285 def foo(): 286 def bar(): 287 print 'baz' 288 bar() 289 foo()"""))) 290 291 def testIf(self): 292 self.assertEqual((0, 'foo\n'), _GrumpRun(textwrap.dedent("""\ 293 if 123: 294 print 'foo' 295 if '': 296 print 'bar'"""))) 297 298 def testIfElif(self): 299 self.assertEqual((0, 'foo\nbar\n'), _GrumpRun(textwrap.dedent("""\ 300 if True: 301 print 'foo' 302 elif False: 303 print 'bar' 304 if False: 305 print 'foo' 306 elif True: 307 print 'bar'"""))) 308 309 def testIfElse(self): 310 self.assertEqual((0, 'foo\nbar\n'), _GrumpRun(textwrap.dedent("""\ 311 if True: 312 print 'foo' 313 else: 314 print 'bar' 315 if False: 316 print 'foo' 317 else: 318 print 'bar'"""))) 319 320 def testImport(self): 321 self.assertEqual((0, "<type 'dict'>\n"), _GrumpRun(textwrap.dedent("""\ 322 import sys 323 print type(sys.modules)"""))) 324 325 def testImportFutureLateRaises(self): 326 regexp = 'from __future__ imports must occur at the beginning of the file' 327 self.assertRaisesRegexp(util.ImportError, regexp, _ParseAndVisit, 328 'foo = bar\nfrom __future__ import print_function') 329 330 def testFutureUnicodeLiterals(self): 331 want = "u'foo'\n" 332 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 333 from __future__ import unicode_literals 334 print repr('foo')"""))) 335 336 def testImportMember(self): 337 self.assertEqual((0, "<type 'dict'>\n"), _GrumpRun(textwrap.dedent("""\ 338 from sys import modules 339 print type(modules)"""))) 340 341 def testImportConflictingPackage(self): 342 self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\ 343 import time 344 from "__go__/time" import Now"""))) 345 346 def testImportNative(self): 347 self.assertEqual((0, '1 1000000000\n'), _GrumpRun(textwrap.dedent("""\ 348 from "__go__/time" import Nanosecond, Second 349 print Nanosecond, Second"""))) 350 351 def testImportGrumpy(self): 352 self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\ 353 from "__go__/grumpy" import Assert 354 Assert(__frame__(), True, 'bad')"""))) 355 356 def testImportNativeType(self): 357 self.assertEqual((0, "<type 'Duration'>\n"), _GrumpRun(textwrap.dedent("""\ 358 from "__go__/time" import Duration 359 print Duration"""))) 360 361 def testPrintStatement(self): 362 self.assertEqual((0, 'abc 123\nfoo bar\n'), _GrumpRun(textwrap.dedent("""\ 363 print 'abc', 364 print '123' 365 print 'foo', 'bar'"""))) 366 367 def testImportWildcard(self): 368 result = _GrumpRun(textwrap.dedent("""\ 369 from time import * 370 print sleep""")) 371 self.assertEqual(0, result[0]) 372 self.assertIn('<function sleep at', result[1]) 373 374 def testImportTryExcept(self): 375 result = _GrumpRun(textwrap.dedent("""\ 376 try: 377 import inexistantmodule 378 except ImportError: 379 from time import sleep as inexistantmodule 380 print inexistantmodule 381 """)) 382 self.assertEqual(0, result[0]) 383 self.assertIn('<function sleep at', result[1]) 384 385 def testImportFromTryExcept(self): 386 result = _GrumpRun(textwrap.dedent("""\ 387 try: 388 from time import inexistantfunction 389 except ImportError: 390 from time import sleep 391 print sleep 392 """)) 393 self.assertEqual(0, result[0]) 394 self.assertIn('<function sleep at', result[1]) 395 396 def testPrintFunction(self): 397 want = "abc\n123\nabc 123\nabcx123\nabc 123 " 398 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 399 "module docstring is ok to proceed __future__" 400 from __future__ import print_function 401 print('abc') 402 print(123) 403 print('abc', 123) 404 print('abc', 123, sep='x') 405 print('abc', 123, end=' ')"""))) 406 407 def testModuleDocstring(self): 408 want = "__doc__ (unicode) is module docstring\n" 409 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 410 from __future__ import unicode_literals 411 "module docstring" 412 print "__doc__ (" + type(__doc__).__name__ + ") is " + str(__doc__)""" 413 ))) 414 415 def testModuleDocstringAbsent(self): 416 want = "__doc__ (NoneType) is None\n" 417 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 418 from __future__ import unicode_literals 419 print "__doc__ (" + type(__doc__).__name__ + ") is " + str(__doc__)""" 420 ))) 421 422 def testClassDocstring(self): 423 want = "Foo.__doc__ (unicode) is class docstring\n" 424 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 425 from __future__ import unicode_literals 426 "module docstring" 427 428 class Foo(object): 429 "class docstring" 430 pass 431 432 print "Foo.__doc__ (" + type(Foo.__doc__).__name__ + ") is " + str(Foo.__doc__)""" 433 ))) 434 435 @pytest.mark.xfail 436 def testClassDocstringAbsent(self): 437 want = "Foo.__doc__ (NoneType) is None\n" 438 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 439 from __future__ import unicode_literals 440 "module docstring" 441 442 class Foo(object): 443 pass 444 445 print "Foo.__doc__ (" + type(Foo.__doc__).__name__ + ") is " + str(Foo.__doc__)""" 446 ))) 447 448 @pytest.mark.xfail 449 def testFunctionDocstring(self): 450 want = "Foo.func.__doc__ (unicode) is function docstring\n" 451 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 452 from __future__ import unicode_literals 453 "module docstring" 454 455 class Foo(object): 456 "class docstring" 457 458 def func(self): 459 "function docstring" 460 return 461 462 print "Foo.func.__doc__ (" + type(Foo.__doc__).__name__ + ") is " + str(Foo.func.__doc__)""" 463 ))) 464 465 def testFunctionDocstringAbsent(self): 466 want = "Foo.func.__doc__ (NoneType) is None\n" 467 self.assertEqual((0, want), _GrumpRun(textwrap.dedent("""\ 468 from __future__ import unicode_literals 469 "module docstring" 470 471 class Foo(object): 472 "class docstring" 473 474 def func(self): 475 return 476 477 print "Foo.func.__doc__ (" + type(Foo.func.__doc__).__name__ + ") is " + str(Foo.func.__doc__)""" 478 ))) 479 480 def testRaiseExitStatus(self): 481 self.assertEqual(1, _GrumpRun('raise Exception')[0]) 482 483 def testRaiseInstance(self): 484 self.assertEqual((0, 'foo\n'), _GrumpRun(textwrap.dedent("""\ 485 try: 486 raise RuntimeError('foo') 487 print 'bad' 488 except RuntimeError as e: 489 print e"""))) 490 491 def testRaiseTypeAndArg(self): 492 self.assertEqual((0, 'foo\n'), _GrumpRun(textwrap.dedent("""\ 493 try: 494 raise KeyError('foo') 495 print 'bad' 496 except KeyError as e: 497 print e"""))) 498 499 def testRaiseAgain(self): 500 self.assertEqual((0, 'foo\n'), _GrumpRun(textwrap.dedent("""\ 501 try: 502 try: 503 raise AssertionError('foo') 504 except AssertionError: 505 raise 506 except Exception as e: 507 print e"""))) 508 509 def testRaiseTraceback(self): 510 self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\ 511 import sys 512 try: 513 try: 514 raise Exception 515 except: 516 e, _, tb = sys.exc_info() 517 raise e, None, tb 518 except: 519 e2, _, tb2 = sys.exc_info() 520 assert e is e2 521 assert tb is tb2"""))) 522 523 def testReturn(self): 524 self.assertEqual((0, 'bar\n'), _GrumpRun(textwrap.dedent("""\ 525 def foo(): 526 return 'bar' 527 print foo()"""))) 528 529 def testTryBareExcept(self): 530 self.assertEqual((0, ''), _GrumpRun(textwrap.dedent("""\ 531 try: 532 raise AssertionError 533 except: 534 pass"""))) 535 536 def testTryElse(self): 537 self.assertEqual((0, 'foo baz\n'), _GrumpRun(textwrap.dedent("""\ 538 try: 539 print 'foo', 540 except: 541 print 'bar' 542 else: 543 print 'baz'"""))) 544 545 def testTryMultipleExcept(self): 546 self.assertEqual((0, 'bar\n'), _GrumpRun(textwrap.dedent("""\ 547 try: 548 raise AssertionError 549 except RuntimeError: 550 print 'foo' 551 except AssertionError: 552 print 'bar' 553 except: 554 print 'baz'"""))) 555 556 def testTryFinally(self): 557 result = _GrumpRun(textwrap.dedent("""\ 558 try: 559 print 'foo', 560 finally: 561 print 'bar' 562 try: 563 print 'foo', 564 raise Exception 565 finally: 566 print 'bar'""")) 567 self.assertEqual(1, result[0]) 568 self.assertIn('foo bar\nfoo bar\n', result[1]) 569 self.assertIn('Exception\n', result[1]) 570 571 def testWhile(self): 572 self.assertEqual((0, '2\n1\n'), _GrumpRun(textwrap.dedent("""\ 573 i = 2 574 while i: 575 print i 576 i -= 1"""))) 577 578 def testWhileElse(self): 579 self.assertEqual((0, 'bar\n'), _GrumpRun(textwrap.dedent("""\ 580 while False: 581 print 'foo' 582 else: 583 print 'bar'"""))) 584 585 def testWith(self): 586 self.assertEqual((0, 'enter\n1\nexit\nenter\n2\nexit\n3\n'), 587 _GrumpRun(textwrap.dedent("""\ 588 class ContextManager(object): 589 def __enter__(self): 590 print "enter" 591 592 def __exit__(self, exc_type, value, traceback): 593 print "exit" 594 595 a = ContextManager() 596 597 with a: 598 print 1 599 600 try: 601 with a: 602 print 2 603 raise RuntimeError 604 except RuntimeError: 605 print 3 606 """))) 607 608 def testWithAsMultiple(self): 609 self.assertEqual((0, '1 2 3\n1 2 3\n'), 610 _GrumpRun(textwrap.dedent("""\ 611 class ContextManager(object): 612 def __enter__(self): 613 return (1, (2, 3)) 614 def __exit__(self, *args): 615 pass 616 with ContextManager() as [x, (y, z)], ContextManager() as [x2, (y2, z2)]: 617 print x, y, z 618 print x2, y2, z2 619 """))) 620 621 def testWithAs(self): 622 self.assertEqual((0, '1 2 3\n'), 623 _GrumpRun(textwrap.dedent("""\ 624 class ContextManager(object): 625 def __enter__(self): 626 return (1, (2, 3)) 627 def __exit__(self, *args): 628 pass 629 with ContextManager() as [x, (y, z)]: 630 print x, y, z 631 """))) 632 633 def testWriteExceptDispatcherBareExcept(self): 634 visitor = stmt.StatementVisitor(_MakeModuleBlock()) 635 handlers = [ast.ExceptHandler(type=ast.Name(id='foo')), 636 ast.ExceptHandler(type=None)] 637 self.assertEqual(visitor._write_except_dispatcher( # pylint: disable=protected-access 638 'exc', 'tb', handlers), [1, 2]) 639 expected = re.compile(r'ResolveGlobal\(.*foo.*\bIsInstance\(.*' 640 r'goto Label1.*goto Label2', re.DOTALL) 641 self.assertRegexpMatches(visitor.writer.getvalue(), expected) 642 643 def testWriteExceptDispatcherBareExceptionNotLast(self): 644 visitor = stmt.StatementVisitor(_MakeModuleBlock()) 645 handlers = [ast.ExceptHandler(type=None), 646 ast.ExceptHandler(type=ast.Name(id='foo'))] 647 self.assertRaisesRegexp(util.ParseError, r"default 'except:' must be last", 648 visitor._write_except_dispatcher, # pylint: disable=protected-access 649 'exc', 'tb', handlers) 650 651 def testWriteExceptDispatcherMultipleExcept(self): 652 visitor = stmt.StatementVisitor(_MakeModuleBlock()) 653 handlers = [ast.ExceptHandler(type=ast.Name(id='foo')), 654 ast.ExceptHandler(type=ast.Name(id='bar'))] 655 self.assertEqual(visitor._write_except_dispatcher( # pylint: disable=protected-access 656 'exc', 'tb', handlers), [1, 2]) 657 expected = re.compile( 658 r'ResolveGlobal\(.*foo.*\bif .*\bIsInstance\(.*\{.*goto Label1.*' 659 r'ResolveGlobal\(.*bar.*\bif .*\bIsInstance\(.*\{.*goto Label2.*' 660 r'\bRaise\(exc\.ToObject\(\), nil, tb\.ToObject\(\)\)', re.DOTALL) 661 self.assertRegexpMatches(visitor.writer.getvalue(), expected) 662 663 664 def _MakeModuleBlock(): 665 return block.ModuleBlock(None, '__main__', '<test>', '', 666 imputil.FutureFeatures()) 667 668 669 def _ParseAndVisit(source): 670 mod = pythonparser.parse(source) 671 _, future_features = imputil.parse_future_features(mod) 672 importer = imputil.Importer(None, 'foo', 'foo.py', False) 673 b = block.ModuleBlock(importer, '__main__', '<test>', 674 source, future_features) 675 visitor = stmt.StatementVisitor(b) 676 visitor.visit(mod) 677 return visitor 678 679 680 def _GrumpRun(cmd): 681 p = subprocess.Popen(['grumpy', 'run'], stdin=subprocess.PIPE, 682 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 683 out, _ = p.communicate(cmd) 684 return p.returncode, out 685 686 687 if __name__ == '__main__': 688 shard_test.main()