def better_exec(script, globals_=None, locals_=None): parsed = import_expression.parse(script) base_function = "async def __evaluate_code(): pass" parsed_function = import_expression.parse(base_function) for node in parsed.body: ast.increment_lineno(node) def check_for_yield(payload): if isinstance(payload, (list, tuple)): for node_ in payload: if check_for_yield(node_): return True if isinstance(payload, (ast.Yield, ast.YieldFrom)): return True if hasattr(payload, 'body'): for node_ in payload.body: if check_for_yield(node_): return True if hasattr(payload, 'value'): if check_for_yield(payload.value): return True return False if not check_for_yield(parsed.body): insert_returns(parsed.body) parsed_function.body[0].body = parsed.body import_expression.exec( import_expression.compile(parsed_function, filename="<evaluator>", mode='exec'), globals_, locals_ )
def wrap_code(code_input): code_in = import_expression.parse(code_input) base = import_expression.parse(code_base) try_block = base.body[-1].body[-1].body try_block.extend(code_in.body) ast.fix_missing_locations(base) insert_yield(try_block) return base
def wrap_code(code: str, args: str = '') -> ast.Module: """ Compiles Python code into an async function or generator, and automatically adds return if the function body is a single evaluation. Also adds inline import expression support. """ if sys.version_info >= (3, 7): user_code = import_expression.parse(code, mode='exec') injected = '' else: injected = code mod = import_expression.parse(CORO_CODE.format(args, textwrap.indent(injected, ' ' * 8)), mode='exec') definition = mod.body[-1] # async def ...: assert isinstance(definition, ast.AsyncFunctionDef) try_block = definition.body[-1] # try: assert isinstance(try_block, ast.Try) if sys.version_info >= (3, 7): try_block.body.extend(user_code.body) else: ast.increment_lineno(mod, -16) # bring line numbers back in sync with repl ast.fix_missing_locations(mod) KeywordTransformer().generic_visit(try_block) last_expr = try_block.body[-1] # if the last part isn't an expression, ignore it if not isinstance(last_expr, ast.Expr): return mod # if the last expression is not a yield if not isinstance(last_expr.value, ast.Yield): # copy the value of the expression into a yield yield_stmt = ast.Yield(last_expr.value) ast.copy_location(yield_stmt, last_expr) # place the yield into its own expression yield_expr = ast.Expr(yield_stmt) ast.copy_location(yield_expr, last_expr) # place the yield where the original expression was try_block.body[-1] = yield_expr return mod
def create_tree(code: str, use_ansi: bool = True): """ Compiles code into an AST tree and then formats it """ user_code = import_expression.parse(code, mode='exec') return '\n'.join(format_ast_node(user_code, use_ansi=use_ansi))
def decode(b, errors='strict'): if not b: return '', 0 decoded = codecs.decode(b, errors=errors, encoding='utf-8') parsed = ie.parse(decoded) parsed.body.insert(0, IMPORT_STATEMENT) unparsed = astunparse.unparse(parsed) return unparsed, len(decoded)
def wrap_code(code: str, args: str = "") -> ast.Module: """ Compiles Python code into an async function or generator, and automatically adds return if the function body is a single evaluation. Also adds inline import expression support. """ user_code = import_expression.parse(code, mode="exec") mod = import_expression.parse(CORO_CODE.format(args), mode="exec") definition = mod.body[-1] # async def ...: assert isinstance(definition, ast.AsyncFunctionDef) try_block = definition.body[-1] # try: assert isinstance(try_block, ast.Try) try_block.body.extend(user_code.body) ast.fix_missing_locations(mod) KeywordTransformer().generic_visit(try_block) last_expr = try_block.body[-1] # if the last part isn't an expression, ignore it if not isinstance(last_expr, ast.Expr): return mod # if the last expression is not a yield if not isinstance(last_expr.value, ast.Yield): # copy the value of the expression into a yield yield_stmt = ast.Yield(last_expr.value) ast.copy_location(yield_stmt, last_expr) # place the yield into its own expression yield_expr = ast.Expr(yield_stmt) ast.copy_location(yield_expr, last_expr) # place the yield where the original expression was try_block.body[-1] = yield_expr return mod
def wrap_code(code: str, args: str = '') -> ast.Module: """ Wraps code for disassembly. This is similar in function to the jishaku.repl.compilation equivalent, but due to the different structure required for clean disassemblies, it's implemented separately here. """ user_code = import_expression.parse(code, mode='exec') mod = import_expression.parse(CORO_CODE.format(args), mode='exec') definition = mod.body[-1] # async def ...: assert isinstance(definition, ast.AsyncFunctionDef) # Patch user code directly into the function definition.body = user_code.body ast.fix_missing_locations(mod) # We do not use the keyword transformer here, since it might produce misleading disassembly. is_asyncgen = any( isinstance(node, ast.Yield) for node in ast.walk(definition)) last_expr = definition.body[-1] # if the last part isn't an expression, ignore it if not isinstance(last_expr, ast.Expr): return mod # if this isn't a generator and the last expression is not a return if not is_asyncgen and not isinstance(last_expr.value, ast.Return): # copy the value of the expression into a return return_stmt = ast.Return(last_expr.value) ast.copy_location(return_stmt, last_expr) # place the return where the original expression was definition.body[-1] = return_stmt return mod
def maybe_add_return(code: str, args: str = '') -> ast.Module: """ Compiles Python code into an async function or generator, and automatically adds return if the function body is a single evaluation. Also adds inline import expression support. """ mod = import_expression.parse(get_wrapped_code(code, args=args), mode='exec') ast.increment_lineno(mod, -12) # bring line numbers back in sync with repl definition = mod.body[-1] # async def ...: assert isinstance(definition, ast.AsyncFunctionDef) try_block = definition.body[-1] # try: assert isinstance(try_block, ast.Try) is_asyncgen = any( isinstance(node, ast.Yield) for node in ast.walk(try_block)) last_expr = try_block.body[-1] # if the last part isn't an expression, ignore it if not isinstance(last_expr, ast.Expr): return mod # if the last expression is not a yield if not isinstance(last_expr.value, ast.Yield): # copy the expression into a return/yield if is_asyncgen: # copy the value of the expression into a yield yield_stmt = ast.Yield(last_expr.value) ast.copy_location(yield_stmt, last_expr) # place the yield into its own expression yield_expr = ast.Expr(yield_stmt) ast.copy_location(yield_expr, last_expr) # place the yield where the original expression was try_block.body[-1] = yield_expr else: # copy the expression into a return return_stmt = ast.Return(last_expr.value) ast.copy_location(return_stmt, last_expr) # place the return where the original expression was try_block.body[-1] = return_stmt return mod
async def eval_(self, ctx, *, code): rle = random.choice(self.bot.loading_emotes) embed = discord.Embed(title="En cours...", description=f"<{rle}>", color=EMBED_COLOUR, timestamp=datetime.utcnow()) message = await ctx.send(embed=embed) timer = Timer() env = { 'bot': self.bot, 'ctx': ctx, 'channel': ctx.channel, 'author': ctx.author, 'guild': ctx.guild, 'message': ctx.message, "_": self._last_result, "__timer": timer } env.update(globals()) code = self.cleanup_code(code) stdout = io.StringIO() to_parse = CORO_CODE.format(textwrap.indent(code, " " * 8)) try: parsed = import_expression.parse(to_parse, mode="exec") exec(compile(parsed, "<eval>", "exec"), env) except Exception as e: embed.title = "Erreur lors de la compilation" embed.description = f'```fix\n{e.__class__.__name__}: {e}\n```' await message.edit(embed=embed) await ctx.tick(False) await ctx.add_delete_reaction(message) return func = env["func"] try: with redirect_stdout(stdout): ret = await func() except Exception: value = stdout.getvalue() embed.title = "Erreur lors de l'éxecution" desc = f"{value}{traceback.format_exc()}" if len(desc) > 2015: desc = desc[:2015] + "..." embed.description = f"```py\n{desc}\n```" await message.edit(embed=embed) await ctx.tick(False) await ctx.add_delete_reaction(message) else: value = stdout.getvalue() try: await ctx.tick() except discord.Forbidden: pass embed.title = "Succès!" embed.description = f"En {round(timer.elapsed, 3)}s" if ret is None: if value: text = value else: await message.edit(embed=embed) await ctx.add_delete_reaction(message) return else: self._last_result = ret text = f"{value}{ret}" class EvalPages(TextPages): async def stop_pages(self): await message.delete() await super().stop_pages() p = EvalPages(ctx, text, prefix="```py", suffix="```", stop_deletes=True) await message.edit(embed=embed) await p.paginate()
def wrap_code(code: str, args: str = '') -> ast.Module: """Compiles Python code into an async function or generator. Automatically adds return if the function body is a single evaluation. Also adds inline import expression support. """ if sys.version_info >= (3, 7): user_code = import_expression.parse(code, mode='exec') injected = '' else: injected = code mod = import_expression.parse(CORO_CODE.format( args, textwrap.indent(injected, ' ' * 8)), mode='exec') definition = mod.body[-1] # async def ...: assert isinstance(definition, ast.AsyncFunctionDef) try_block = definition.body[-1] # try: assert isinstance(try_block, ast.Try) if sys.version_info >= (3, 7): try_block.body.extend(user_code.body) else: preclude_offset = CORO_CODE.split('pass')[0].count('\n') + 1 ast.increment_lineno( mod, -preclude_offset) # bring line numbers back in sync with repl ast.fix_missing_locations(mod) is_asyncgen = any( isinstance(node, ast.Yield) for node in ast.walk(try_block)) last_expr = try_block.body[-1] # if the last part isn't an expression, ignore it if not isinstance(last_expr, ast.Expr): return mod # if the last expression is not a yield if not isinstance(last_expr.value, ast.Yield): # copy the expression into a return/yield if is_asyncgen: # copy the value of the expression into a yield yield_stmt = ast.Yield(last_expr.value) ast.copy_location(yield_stmt, last_expr) # place the yield into its own expression yield_expr = ast.Expr(yield_stmt) ast.copy_location(yield_expr, last_expr) # place the yield where the original expression was try_block.body[-1] = yield_expr else: # copy the expression into a return return_stmt = ast.Return(last_expr.value) ast.copy_location(return_stmt, last_expr) # place the return where the original expression was try_block.body[-1] = return_stmt return mod