|
1368 | 1368 | {
|
1369 | 1369 | "cell_type": "code",
|
1370 | 1370 | "execution_count": null,
|
1371 |
| - "id": "84cadbec-0b34-4669-bfc9-6bf790816035", |
| 1371 | + "id": "1574585f", |
1372 | 1372 | "metadata": {},
|
1373 | 1373 | "outputs": [],
|
1374 | 1374 | "source": [
|
1375 | 1375 | "#| exports\n",
|
1376 |
| - "def _run(code:str ):\n", |
| 1376 | + "def _run(code:str, glb:dict=None, loc:dict=None):\n", |
1377 | 1377 | " \"Run `code`, returning final expression (similar to IPython)\"\n",
|
1378 | 1378 | " tree = ast.parse(code)\n",
|
1379 | 1379 | " last_node = tree.body[-1] if tree.body else None\n",
|
|
1385 | 1385 | " tree.body[-1] = _copy_loc(assign_node, last_node)\n",
|
1386 | 1386 | "\n",
|
1387 | 1387 | " compiled_code = compile(tree, filename='<ast>', mode='exec')\n",
|
1388 |
| - " namespace = {}\n", |
| 1388 | + " glb = glb or {}\n", |
1389 | 1389 | " stdout_buffer = io.StringIO()\n",
|
1390 | 1390 | " saved_stdout = sys.stdout\n",
|
1391 | 1391 | " sys.stdout = stdout_buffer\n",
|
1392 |
| - " try: exec(compiled_code, namespace)\n", |
| 1392 | + " try: exec(compiled_code, glb, loc)\n", |
1393 | 1393 | " finally: sys.stdout = saved_stdout\n",
|
1394 |
| - " _result = namespace.get('_result', None)\n", |
| 1394 | + " _result = glb.get('_result', None)\n", |
1395 | 1395 | " if _result is not None: return _result\n",
|
1396 | 1396 | " return stdout_buffer.getvalue().strip()"
|
1397 | 1397 | ]
|
|
1462 | 1462 | "outputs": [],
|
1463 | 1463 | "source": [
|
1464 | 1464 | "#| exports\n",
|
1465 |
| - "def python(code, # Code to execute\n", |
1466 |
| - " timeout=5 # Maximum run time in seconds before a `TimeoutError` is raised\n", |
| 1465 | + "def python(code:str, # Code to execute\n", |
| 1466 | + " glb:Optional[dict]=None, # Globals namespace\n", |
| 1467 | + " loc:Optional[dict]=None, # Locals namespace\n", |
| 1468 | + " timeout:int=3600 # Maximum run time in seconds before a `TimeoutError` is raised\n", |
1467 | 1469 | " ): # Result of last node, if it's an expression, or `None` otherwise\n",
|
1468 | 1470 | " \"\"\"Executes python `code` with `timeout` and returning final expression (similar to IPython).\n",
|
1469 | 1471 | " Raised exceptions are returned as a string, with a stack trace.\"\"\"\n",
|
1470 | 1472 | " def handler(*args): raise TimeoutError()\n",
|
| 1473 | + " if glb is None: glb = inspect.currentframe().f_back.f_globals\n", |
| 1474 | + " if loc is None: loc=glb\n", |
1471 | 1475 | " signal.signal(signal.SIGALRM, handler)\n",
|
1472 | 1476 | " signal.alarm(timeout)\n",
|
1473 |
| - " try: return _run(code)\n", |
| 1477 | + " try: return _run(code, glb, loc)\n", |
1474 | 1478 | " except Exception as e: return traceback.format_exc()\n",
|
1475 | 1479 | " finally: signal.alarm(0)"
|
1476 | 1480 | ]
|
|
1512 | 1516 | "id": "6c629442",
|
1513 | 1517 | "metadata": {},
|
1514 | 1518 | "source": [
|
1515 |
| - "If the code takes longer than `timeout` then it raises a `TimeoutError`." |
| 1519 | + "If the code takes longer than `timeout` then it returns an error string." |
1516 | 1520 | ]
|
1517 | 1521 | },
|
1518 | 1522 | {
|
1519 | 1523 | "cell_type": "code",
|
1520 | 1524 | "execution_count": null,
|
1521 | 1525 | "id": "fcb472b3",
|
1522 | 1526 | "metadata": {},
|
1523 |
| - "outputs": [], |
| 1527 | + "outputs": [ |
| 1528 | + { |
| 1529 | + "name": "stdout", |
| 1530 | + "output_type": "stream", |
| 1531 | + "text": [ |
| 1532 | + "Traceback (most recent call last):\n", |
| 1533 | + " File \"/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_12053/775515280.py\", line 12, in python\n", |
| 1534 | + " try: return _run(code, glb, loc)\n", |
| 1535 | + " ^^^^^^^^^^^^^^^^^^^^\n", |
| 1536 | + " File \"/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_12053/1858893181.py\", line 18, in _run\n", |
| 1537 | + " try: exec(compiled_code, glb, loc)\n", |
| 1538 | + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", |
| 1539 | + " File \"<ast>\", line 1, in <module>\n", |
| 1540 | + " File \"/var/folders/51/b2_szf2945n072c0vj2cyty40000gn/T/ipykernel_12053/775515280.py\", line 9, in handler\n", |
| 1541 | + " def handler(*args): raise TimeoutError()\n", |
| 1542 | + " ^^^^^^^^^^^^^^^^^^^^\n", |
| 1543 | + "TimeoutError\n", |
| 1544 | + "\n" |
| 1545 | + ] |
| 1546 | + } |
| 1547 | + ], |
| 1548 | + "source": [ |
| 1549 | + "print(python('import time; time.sleep(10)', timeout=1))" |
| 1550 | + ] |
| 1551 | + }, |
| 1552 | + { |
| 1553 | + "cell_type": "markdown", |
| 1554 | + "id": "d45684c1", |
| 1555 | + "metadata": {}, |
| 1556 | + "source": [ |
| 1557 | + "By default the caller's global namespace is used." |
| 1558 | + ] |
| 1559 | + }, |
| 1560 | + { |
| 1561 | + "cell_type": "code", |
| 1562 | + "execution_count": null, |
| 1563 | + "id": "72dfe290", |
| 1564 | + "metadata": {}, |
| 1565 | + "outputs": [ |
| 1566 | + { |
| 1567 | + "data": { |
| 1568 | + "text/plain": [ |
| 1569 | + "1" |
| 1570 | + ] |
| 1571 | + }, |
| 1572 | + "execution_count": null, |
| 1573 | + "metadata": {}, |
| 1574 | + "output_type": "execute_result" |
| 1575 | + } |
| 1576 | + ], |
| 1577 | + "source": [ |
| 1578 | + "python(\"a=1\")\n", |
| 1579 | + "a" |
| 1580 | + ] |
| 1581 | + }, |
| 1582 | + { |
| 1583 | + "cell_type": "markdown", |
| 1584 | + "id": "bf48557c", |
| 1585 | + "metadata": {}, |
| 1586 | + "source": [ |
| 1587 | + "Pass a different `glb` if needed." |
| 1588 | + ] |
| 1589 | + }, |
| 1590 | + { |
| 1591 | + "cell_type": "code", |
| 1592 | + "execution_count": null, |
| 1593 | + "id": "55fb5613", |
| 1594 | + "metadata": {}, |
| 1595 | + "outputs": [ |
| 1596 | + { |
| 1597 | + "data": { |
| 1598 | + "text/plain": [ |
| 1599 | + "(1, 3)" |
| 1600 | + ] |
| 1601 | + }, |
| 1602 | + "execution_count": null, |
| 1603 | + "metadata": {}, |
| 1604 | + "output_type": "execute_result" |
| 1605 | + } |
| 1606 | + ], |
1524 | 1607 | "source": [
|
1525 |
| - "try: python('import time; time.sleep(10)', timeout=1)\n", |
1526 |
| - "except TimeoutError: print('Timed out')" |
| 1608 | + "glb = {}\n", |
| 1609 | + "python(\"a=3\", glb)\n", |
| 1610 | + "a, glb['a']" |
1527 | 1611 | ]
|
1528 | 1612 | },
|
1529 | 1613 | {
|
|
0 commit comments