| 90 | |
| 91 | |
| 92 | class PyExecNamespace(): |
| 93 | def __init__(self, globals=None, locals=None): |
| 94 | self.globals = globals or {} |
| 95 | self.locals = locals or {} |
| 96 | |
| 97 | def exec(self, source): |
| 98 | """ |
| 99 | Execute code in namespace. |
| 100 | |
| 101 | If code outputs to stdout, that output is captured and returned. |
| 102 | If nothing it output to stdout, then the last line of code is checked |
| 103 | for a variable assignment. If one exists, then the value of that variable |
| 104 | is returned. If that fails, then `None` is returned. |
| 105 | """ |
| 106 | |
| 107 | # Temporarily redirect stdout |
| 108 | save_stdout = sys.stdout |
| 109 | sys.stdout = StringIO() |
| 110 | |
| 111 | # Run code |
| 112 | try: |
| 113 | exec(source, self.globals, self.locals) |
| 114 | except KeyboardInterrupt: |
| 115 | sys.stdout.close() |
| 116 | sys.stdout = save_stdout |
| 117 | raise |
| 118 | except BaseException as exc: |
| 119 | sys.stdout.close() |
| 120 | sys.stdout = save_stdout |
| 121 | import traceback |
| 122 | tb = traceback.format_exception(exc, exc, exc.__traceback__.tb_next) |
| 123 | return 'traceback', '\n'.join(tb) |
| 124 | |
| 125 | # Retreive anything sent to stdout and restore system default |
| 126 | out = sys.stdout.getvalue() |
| 127 | sys.stdout.close() |
| 128 | sys.stdout = save_stdout |
| 129 | |
| 130 | if out: |
| 131 | # Return text sent to stdout |
| 132 | return 'stdout', out |
| 133 | else: |
| 134 | # Nothing sent to stdout. Try to get value of last variable assignment |
| 135 | target = None |
| 136 | a = ast.parse(source) |
| 137 | if a.body: |
| 138 | if isinstance(a_last := a.body[-1], ast.Assign): |
| 139 | target = ast.unparse(a_last.targets[0]) |
| 140 | elif isinstance(a_last, (ast.AnnAssign, ast.AugAssign)): |
| 141 | target = ast.unparse(a_last.target) |
| 142 | if target and target in self.locals: |
| 143 | return target, self.locals[target] |
| 144 | return None, None |
| 145 | |
| 146 | |
| 147 | def py_render(src="", language="", class_name=None, options=None, md="", **kwargs): |