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_ )
async def evaluate(bot, body): env = {"bot": bot} env.update(globals()) stdout = StringIO() to_compile = f'async def func():\n{indent(body, " ")}' try: import_expression.exec(to_compile, env) except Exception as e: return f"```py\n{e.__class__.__name__}: {e}\n```" func = env["func"] try: with redirect_stdout(stdout): ret = await func() except Exception: value = stdout.getvalue() return f"```py\n{value}{format_exc()}\n```" else: value = stdout.getvalue() if ret is None: if value: return f"```py\n{value}\n```" else: return f"```py\n{value}{ret}\n```"
async def eval_command(self, ctx: botto.Context, *, code: str) -> None: """Evaluate a block of code.""" await ctx.message.add_reaction(botto.aLOADING) env = { "bot": self.bot, "ctx": ctx, "channel": ctx.channel, "author": ctx.author, "guild": ctx.guild, "message": ctx.message, "session": self.bot.session, "_": self._last_result, } env.update(globals()) code = self._cleanup_code(code) stdout: io.StringIO = io.StringIO() to_compile: str = f"async def func():\n{textwrap.indent(code, ' ')}" # Defining the async function. try: import_expression.exec(to_compile, env) except Exception as exc: # pylint: disable=broad-except await self.send_eval_error(ctx, exc) return func = env["func"] # Executing the async function. try: start: float = time.perf_counter() with redirect_stdout(stdout): return_value: Any = await func() except Exception as exc: # pylint: disable=broad-except await self.send_eval_error(ctx, exc) return # When code execution is successful time_delta: float = (time.perf_counter() - start) * 1000 stdout_value: str = stdout.getvalue() # Try to unreact try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CHECK) except discord.NotFound: if not stdout_value and return_value is None: await ctx.send( f"{ctx.author.mention} Code execution completed in {time_delta:.2f} ms." ) if not stdout_value and return_value is None: return if return_value is not None: self._last_result = return_value await self.reply_eval_completed(ctx, code, stdout_value, return_value, time_delta)
def test_del_store_attribute(): class AttributeBox: pass x = AttributeBox() g = dict(x=x) ie.exec('x.y = 1', g) assert x.y == 1 ie.exec('del x.y', g) assert not hasattr(x, 'y')
async def eval(self, ctx, *, code: str): env = { "ctx": ctx, "author": ctx.author, "message": ctx.message, "guild": ctx.guild, "bot": self.bot, "reference": ctx.message.reference, "resolved": ctx.message.reference.resolved if ctx.message.reference else None, } env.update(globals()) imports = "import asyncio\n" "import discord\nfrom discord.ext import commands\nimport aiohttp\n" body = "async def func():\n" + textwrap.indent(imports + code, " ") try: import_expression.exec(body, env, locals()) except Exception as e: etype = type(e) trace = e.__traceback__ lines = traceback.format_exception(etype, e, trace) paginator = WrappedPaginator(max_size=500, prefix="A weird error occured```py", suffix="```") paginator.add_line("".join(lines)) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) return await interface.send_to(ctx) try: maybe_coro = locals()["func"]() if inspect.isasyncgen(maybe_coro): async for i in maybe_coro: await ctx.send(i) return thing = await maybe_coro if thing: if isinstance(thing, discord.Embed): return await ctx.send(embed=thing) if isinstance(thing, discord.File): return await ctx.send(file=thing) else: await ctx.send(thing) except Exception as e: await ctx.send(e)
async def _eval(self, ctx, *, body: str): """Evaluates a code""" env = { "bot": self.bot, "ctx": ctx, "channel": ctx.channel, "author": ctx.author, "guild": ctx.guild, "message": ctx.message, "__last__": self._last_result, } env.update(globals()) body = self.cleanup_code(body) stdout = io.StringIO() token = random_token(self.bot.user.id) to_compile = f'async def func():\n{textwrap.indent(body, " ")}' try: import_expression.exec(to_compile, env) except Exception as e: return await ctx.send(f"```py\n{e.__class__.__name__}: {e}\n```") func = env["func"] try: with redirect_stdout(stdout): ret = await func() if ret is not None: ret = str(ret).replace(self.bot.http.token, token) except Exception: value = stdout.getvalue() value = value.replace(self.bot.http.token, token) await ctx.send(f"```py\n{value}{traceback.format_exc()}\n```") else: value = stdout.getvalue() value = value.replace(self.bot.http.token, token) try: await ctx.message.add_reaction("blackcheck:441826948919066625") except discord.Forbidden: pass if ret is None: if value: await ctx.send(f"```py\n{value}\n```") else: self._last_result = ret await ctx.send(f"```py\n{value}{ret}\n```")
async def eval_cmd(self, ctx: MyContext, *, body: str): """Evaluates a code""" env: dict[str, typing.Any] = { 'bot': self.bot, 'ctx': ctx, 'channel': ctx.channel, 'author': ctx.author, 'guild': ctx.guild, 'message': ctx.message, '_': self._last_result } env.update(globals()) body = self.cleanup_code(body) stdout = io.StringIO() try: to_compile = f'async def func():\n{textwrap.indent(body, " ")}' exec(to_compile, env) except Exception as e: return await ctx.send(f'```py\n{e.__class__.__name__}: {e}\n```') func: typing.Callable[..., typing.Coroutine] = env['func'] exc: typing.Optional[BaseException] = None ret: typing.Any = None async with ctx.typing(): try: with redirect_stdout(stdout): fut = func() wait = asyncio.create_task(asyncio.wait_for(fut, 60)) self._running_evals[ctx.channel.id] = wait ret = await wait except Exception as e: exc = e finally: _ = self._running_evals.pop(ctx.channel.id, None) await self.send_eval_result( ctx, exc, 'Eval completed successfully', 'Process cancelled' if isinstance( exc, asyncio.CancelledError) else 'An exception has occurred', ret=ret, stdout=stdout.getvalue(), traceback=self.format_tb(exc)) if ret is not None: self._last_result = ret
async def eval_(self, ctx, *, body: CBStripConverter): """Runs code that you input to the command""" env = { 'bot': self.bot, 'ctx': ctx, 'channel': ctx.channel, 'author': ctx.author, 'guild': ctx.guild, 'message': ctx.message, '_': self._last_result } env.update(globals()) if self.retain: env.update(self.scope) stdout = io.StringIO() to_return = None to_compile = f'async def func(scope, should_retain=True):' \ f'\n try:' \ f'\n{textwrap.indent(body, " ")}' \ f'\n finally:' \ f'\n if not should_retain:' \ f'\n return' \ f'\n scope.update(locals())' async with ctx.loading(exc_ignore=HandleTb): try: import_expression.exec(to_compile, env) except Exception as e: raise HandleTb(ctx, e) evaluated_func = env['func'] try: with redirect_stdout(stdout): result = await evaluated_func(self.scope, self.retain) or '' except Exception as e: raise HandleTb(ctx, e) else: value = stdout.getvalue() or '' self._last_result = result to_return = f'{value}{result}' if to_return: pages = group(to_return, 1500) pages = [ctx.codeblock(page, 'py') for page in pages] await ctx.quick_menu(pages, 1, delete_message_after=True, timeout=1800)
async def eval_(self, ctx, *, body: str): """Runs code that you input to the command""" env = { 'bot': self.bot, 'ctx': ctx, 'channel': ctx.channel, 'author': ctx.author, 'guild': ctx.guild, 'message': ctx.message, '_': self._last_result } env.update(globals()) body = cleanup_code(self, body) stdout = io.StringIO() to_compile = f'async def func():\n{textwrap.indent(body, " ")}' try: import_expression.exec(to_compile, env) except Exception as e: return await ctx.send(f'```py\n{e.__class__.__name__}: {e}\n```') func = env['func'] try: with redirect_stdout(stdout): ret = await func() except Exception: value = stdout.getvalue() await ctx.send(f'```py\n{value}{traceback.format_exc()}\n```') else: value = stdout.getvalue() try: await ctx.message.add_reaction('\u2705') except: pass if ret is None: if value: await ctx.send(f'```py\n{value}\n```') else: self._last_result = ret await ctx.send(f'```py\n{value}{ret}\n```')
def test_eval_exec(): import ipaddress import textwrap import urllib.parse assert ie.eval('collections!.Counter(urllib.parse!.quote("foo"))') == dict(f=1, o=2) assert ie.eval('ipaddress!.IPV6LENGTH') == ipaddress.IPV6LENGTH assert ie.eval('urllib.parse!.quote("?")') == urllib.parse.quote('?') g = {} ie.exec(textwrap.dedent(""" a = urllib.parse!.unquote def b(): return operator!.concat(a('%3F'), a('these_tests_are_overkill_for_a_debug_cog%3D1'))""" ), g) assert g['b']() == '?these_tests_are_overkill_for_a_debug_cog=1' g = {} ie.exec(textwrap.dedent(""" def foo(x): x = x + 1 x = x + 1 x = x + 1 x = x + 1 def bar(): return urllib.parse!.unquote('can%20we%20make%20it%20into%20jishaku%3F') # the hanging indent on the following line is intentional return bar()""" ), g) assert g['foo'](1) == 'can we make it into jishaku?'
def test_typehint_conversion(): from typing import T g = {} ie.exec('def foo() -> typing!.T: pass', g) assert g['foo'].__annotations__['return'] is T ie.exec('def bar(x: typing!.T): pass', g) assert g['bar'].__annotations__['x'] is T ie.exec('def baz(x: typing!.T = 1): pass', g) assert g['baz'].__annotations__['x'] is T
def main(): args = parser.parse_args() if sum((args.list_of_stdin, args.lines_of_stdin)) > 1: err('Pythonpy accepts at most one of [-x, -l] flags') sys.exit(1) if args.pre_cmd: import_expression.exec(args.pre_cmd) if not args.expression and args.post_cmd: import_expression.exec(args.post_cmd) sys.exit(0) stdin = map(str.rstrip, sys.stdin) # ignore trailing newline if args.input_delimiter: split = re.compile(args.input_delimiter).split stdin = (split(l.rstrip()) for l in stdin) if args.output_delimiter: def fprint(x, join=args.output_delimiter.join): if x is not None: print(join(x)) else: def fprint(x): if x is not None: print(x) code = import_expression.compile(args.expression, '<pythonpy expression>', 'eval') if args.list_of_stdin: l = list(stdin) it = [import_expression.eval(code, dict(l=l))] elif args.lines_of_stdin: it = (import_expression.eval(code, dict(x=line)) for line in stdin) else: it = [import_expression.eval(code)] for x in it: try: it = [x] if isinstance(x, str) else iter(x) except TypeError: # not iterable fprint(x) else: for x in it: fprint(x) if args.post_cmd: import_expression.exec(args.post_cmd)
def main(): args = parser.parse_args() if sum((args.list_of_stdin, args.lines_of_stdin)) > 1: err('Pythonpy accepts at most one of [-x, -l] flags') sys.exit(1) if args.pre_cmd: import_expression.exec(args.pre_cmd) if not args.expression and args.post_cmd: import_expression.exec(args.post_cmd) sys.exit(0) stdin = without_trailing(map(str.rstrip, sys.stdin), trailing='') # ignore trailing newline code = import_expression.compile(args.expression, '<pythonpy expression>', 'eval') if args.list_of_stdin: l = list(stdin) it = [import_expression.eval(code, dict(l=l))] elif args.lines_of_stdin: it = (import_expression.eval(code, dict(x=line)) for line in stdin) else: it = [import_expression.eval(code)] for x in it: if x is None: continue try: it = [x] if isinstance(x, str) else iter(x) except TypeError: # not iterable print(x) else: for x in it: print(x) if args.post_cmd: import_expression.exec(args.post_cmd)
async def eval_command(self, ctx: botto.Context, *, code: str) -> None: """Evaluate a block of code.""" await ctx.message.add_reaction(botto.aLOADING) env = { "bot": self.bot, "ctx": ctx, "channel": ctx.channel, "author": ctx.author, "guild": ctx.guild, "message": ctx.message, "session": self.bot.session, "_": self._last_result, } env.update(globals()) code = self._cleanup_code(code) stdout: io.StringIO = io.StringIO() to_compile: str = f"async def func():\n{textwrap.indent(code, ' ')}" # Defining the async function. try: import_expression.exec(to_compile, env) except Exception as exc: # pylint: disable=broad-except try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CROSS) except discord.NotFound: pass # Ignore if command message was deleted await ctx.send(f"{type(exc).__name__} occured. Check your code.") return func = env["func"] # Executing the async function. try: start: float = time.perf_counter() with redirect_stdout(stdout): ret: Any = await func() except Exception as exc: # pylint: disable=broad-except try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CROSS) except discord.NotFound: pass # Ignore if command message was deleted await ctx.send(f"{type(exc).__name__} occured.\n{str(exc)}") return # When code execution is successful delta: float = (time.perf_counter() - start) * 1000 value: str = stdout.getvalue() # Try to unreact try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CHECK) except discord.NotFound: if not value and ret is None: await ctx.send( f"{ctx.author.mention} Code execution completed in {delta:.2f} ms." ) if not value and ret is None: return # If there is stdout and return value embed: discord.Embed = discord.Embed( timestamp=ctx.message.created_at, colour=botto.config.MAIN_COLOUR ) embed.set_author(name="Code Evaluation") embed.set_footer( text=f"Took {delta:.2f} ms with Python {platform.python_version()}" ) result: List[str] = ["```py"] to_upload: List[Tuple[str, str]] = [("code.py", code)] if value: result.append(f"# stdout:\n{value}") to_upload.append(("stdout.txt", value)) if ret is not None: self._last_result = ret result.append(f"# return:\n{ret!r}") to_upload.append(("return.py", repr(ret))) result.append("```") result_string: str = "\n".join(result) is_uploaded: bool = False if len(result_string) > 2048: url: str = await ctx.gist( *to_upload, description=( f"Eval command results from {self._get_origin(ctx)} " f"at {embed.timestamp}." ), ) embed.description = f"Results too long. View them [here]({url})." is_uploaded = True else: embed.description = result_string message = await ctx.send(embed=embed) if is_uploaded: await message.add_reaction("\N{WASTEBASKET}")
async def eval(self, ctx, *, code_string): if not self._env: self._env.update({"discord": discord, "commands": commands, '_': None, import_expression.constants.IMPORTER: importlib.import_module}) self._env['ctx'] = ctx try: expr = import_expression.compile(code_string) ret = eval(expr, self._env) except SyntaxError: pass except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.send_as_paginator(format_exc(exc), codeblock=True) else: if inspect.isawaitable(ret): fut = asyncio.ensure_future(ret, loop=self.bot.loop) self._evals.append(fut) try: with Timer(ctx.message): ret = await asyncio.wait_for(fut, timeout=self._timeout) except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.send_as_paginator(format_exc(exc), codeblock=True) finally: self._evals.remove(fut) await ctx.message.add_reaction(self.bot.tick_yes) if ret is None: return self._env['_'] = ret if isinstance(ret, discord.Embed): return await ctx.send(embed=ret) if not isinstance(ret, str): ret = repr(ret) return await ctx.send_as_paginator(ret, codeblock=self._send_in_codeblocks) code = f"""async def __func__(): try: {textwrap.indent(code_string, ' ')} finally: globals().update(locals())""" try: import_expression.exec(code, self._env) except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.send_as_paginator(format_exc(exc), codeblock=True) func = self._env.pop('__func__') fut = asyncio.ensure_future(func(), loop=self.bot.loop) self._evals.append(fut) try: with Timer(ctx.message): await asyncio.wait_for(fut, timeout=self._timeout) except asyncio.CancelledError: await ctx.message.add_reaction('\U0001f6d1') return except asyncio.TimeoutError: await ctx.message.add_reaction('\u23f0') return except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.send_as_paginator(format_exc(exc), codeblock=True) else: ret = fut.result() finally: self._evals.remove(fut) await ctx.message.add_reaction(self.bot.tick_yes) if ret is None: return self._env['_'] = ret if isinstance(ret, discord.Embed): return await ctx.send(embed=ret) if not isinstance(ret, str): ret = repr(ret) return await ctx.send_as_paginator(ret, codeblock=self._send_in_codeblocks)
def test_comments(): ie.exec('# a')
def test_importer_name_not_mangled(): # if import_expression.constants.IMPORTER.startswith('__'), # this will fail ie.exec('class Foo: x = io!')
def __aiter__(self): import_expression.exec(compile(self.code, "<eval>", "exec"), self.context) _aexec = self.context["func"] return self.get_results(_aexec, self.scope)
async def eval_command(self, ctx: botto.Context, *, code: str) -> None: """Evaluate a block of code.""" await ctx.message.add_reaction(botto.aLOADING) env = { "bot": self.bot, "ctx": ctx, "channel": ctx.channel, "author": ctx.author, "guild": ctx.guild, "message": ctx.message, "session": self.bot.session, "_": self._last_result, } env.update(globals()) code = self._cleanup_code(code) stdout: io.StringIO = io.StringIO() to_compile: str = f"async def func():\n{textwrap.indent(code, ' ')}" # Defining the async function. try: import_expression.exec(to_compile, env) except Exception as exc: # pylint: disable=broad-except try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CROSS) except discord.NotFound: pass # Ignore if command message was deleted await ctx.send(f"{type(exc).__name__} occurred. Check your code.") return func = env["func"] # Executing the async function. try: start: float = time.perf_counter() with redirect_stdout(stdout): ret: Any = await func() except Exception as exc: # pylint: disable=broad-except try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CROSS) except discord.NotFound: pass # Ignore if command message was deleted await ctx.send(f"{type(exc).__name__} occurred.\n{str(exc)}") return # When code execution is successful delta: float = (time.perf_counter() - start) * 1000 value: str = stdout.getvalue() # Try to unreact try: await ctx.message.remove_reaction(botto.aLOADING, ctx.me) await ctx.message.add_reaction(botto.CHECK) except discord.NotFound: if not value and ret is None: await ctx.send( f"{ctx.author.mention} Code execution completed in {delta:.2f} ms." ) if not value and ret is None: return # If there is stdout and return value embed: discord.Embed = discord.Embed(timestamp=ctx.message.created_at, color=botto.config["MAIN_COLOR"]) embed.set_author(name="Code Evaluation") embed.set_footer( text=f"Took {delta:.2f} ms with Python {platform.python_version()}" ) result: List[str] = ["```py"] to_upload: List[Tuple[str, str]] = [("code.py", code)] if value: result.append(f"# stdout:\n{value}") to_upload.append(("stdout.txt", value)) if ret is not None: self._last_result = ret result.append(f"# return:\n{ret!r}") to_upload.append(("return.py", repr(ret))) result.append("```") result_string: str = "\n".join(result) uploaded_to: Optional[str] = None file: Optional[discord.File] = None if len(result_string) <= 2048: embed.description = result_string else: try: url: str = await botto.utils.gist( *to_upload, description=( f"Eval command results from {self._get_origin(ctx)} " f"at {embed.timestamp}."), session=self.bot.session, ) except (aiohttp.ClientResponseError, ValueError): try: url = await botto.utils.hastebin( f"Eval command results from {self._get_origin(ctx)} " f"at {embed.timestamp}.\n\n{result_string[6:-4]}", session=self.bot.session, ) except (aiohttp.ClientResponseError, ValueError): # If 8MB is insufficient, give up file = discord.File(io.StringIO(result_string[6:-4]), "results.txt") embed.description = "Results too long. View them in the file." else: uploaded_to = "hastebin" else: uploaded_to = "github" if uploaded_to: embed.description = f"Results too long. View them [here]({url})." message = await ctx.send(embed=embed, file=file) if uploaded_to == "github" and ( (ctx.guild and botto.config["INTENTS"]["GUILD_REACTIONS"]) or (not ctx.guild and botto.config["INTENTS"]["DM_REACTIONS"])): await message.add_reaction("\N{WASTEBASKET}")
async def eval(self, ctx, *, code_string): """ evaluates some code. Do not run this unless you know what you are doing! """ """ The code used in this command is licensed under AGPLv3 clause. Modification or distribution without permission is not granted unless it works with the License. XuaTheGrate - the owner of this code - has granted explicit permission for me to use this code within amalna. The original source code can be located here: https://github.com/iDevision/MostDefinitelyGrant2/blob/master/cogs/dev.py A copy of the AGPLv3 License can be located here: https://github.com/iDevision/MostDefinitelyGrant2/blob/master/LICENSE.md """ if not self._env: self._env.update({ "discord": discord, "twitchio": twitchio, "bot": self.bot, "twitch_streamer": self.core.twitch_streamer, "twitch_bot": self.core.twitch_bot, "db": self.core.db, "commands": commands, '_': None, import_expression.constants.IMPORTER: importlib.import_module}) self._env['ctx'] = ctx try: expr = import_expression.compile(code_string) ret = eval(expr, self._env) except SyntaxError: pass except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.paginate("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)), codeblocks=True) else: if inspect.isawaitable(ret): fut = asyncio.ensure_future(ret, loop=self.bot.loop) self._evals.append(fut) try: with Timer(ctx.message): ret = await asyncio.wait_for(fut, timeout=self._timeout) except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.paginate("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)), codeblocks=True) finally: self._evals.remove(fut) await ctx.message.add_reaction(self.bot.tick_yes) if ret is None: return self._env['_'] = ret if isinstance(ret, discord.Embed): return await ctx.send(embed=ret) if not isinstance(ret, str): ret = repr(ret) return await ctx.paginate(ret, codeblock=self._send_in_codeblocks) code = f"""async def __func__(): try: {textwrap.indent(code_string, ' ')} finally: globals().update(locals())""" try: import_expression.exec(code, self._env) except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.paginate("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)), codeblocks=True) func = self._env.pop('__func__') fut = asyncio.ensure_future(func(), loop=self.bot.loop) self._evals.append(fut) try: with Timer(ctx.message): await asyncio.wait_for(fut, timeout=self._timeout) except asyncio.CancelledError: await ctx.message.add_reaction('\U0001f6d1') return except asyncio.TimeoutError: await ctx.message.add_reaction('\u23f0') return except Exception as exc: await ctx.message.add_reaction(self.bot.tick_no) return await ctx.paginate("".join(traceback.format_exception(type(exc), exc, exc.__traceback__)), codeblocks=True) else: ret = fut.result() finally: self._evals.remove(fut) await ctx.message.add_reaction(self.bot.tick_yes) if ret is None: return self._env['_'] = ret if isinstance(ret, discord.Embed): return await ctx.send(embed=ret) if not isinstance(ret, str): ret = repr(ret) return await ctx.paginate(ret, codeblocks=self._send_in_codeblocks)