async def jsk_python(self, ctx: commands.Context, *, argument: codeblock_converter): """ Direct evaluation of Python code. """ arg_dict = get_var_dict_from_ctx(ctx, Flags.SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(argument.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): if result is None: continue self.last_result = result send(await self.jsk_python_result_handling(ctx, result)) finally: scope.clear_intersection(arg_dict)
async def jsk_python(self, ctx: commands.Context, *, argument: codeblock_converter): """ Direct evaluation of Python code. """ arg_dict = get_var_dict_from_ctx(ctx, SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor( argument.content, scope, arg_dict=arg_dict ) async for send, result in AsyncSender(executor): if result is None: continue self.last_result = result if isinstance(result, discord.File): send(await ctx.send(file=result)) elif isinstance(result, discord.Embed): send(await ctx.send(embed=result)) elif isinstance(result, PaginatorInterface): send(await result.send_to(ctx)) else: if not isinstance(result, str): # repr all non-strings result = repr(result) if len(result) > 2000: # inconsistency here, results get wrapped in codeblocks when they are too large # but don't if they're not. probably not that bad, but noting for later review paginator = WrappedPaginator( prefix="```py", suffix="```", max_size=1985 ) paginator.add_line(result) interface = PaginatorInterface( ctx.bot, paginator, owner=ctx.author ) send(await interface.send_to(ctx)) else: if result.strip() == "": result = "\u200b" send( await ctx.send( result.replace( self.bot.http.token, "[token omitted]", ) ) ) finally: scope.clear_intersection(arg_dict)
async def jsk_python_inspect(self, ctx: commands.Context, *, argument: codeblock_converter): """ Evaluation of Python code with inspect information. """ arg_dict = get_var_dict_from_ctx(ctx, SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(argument.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): self.last_result = result header = repr(result).replace("``", "`\u200b`").replace(self.bot.http.token, "[token omitted]") if len(header) > 485: header = header[0:482] + "..." paginator = WrappedPaginator(prefix=f"```prolog\n=== {header} ===\n", max_size=1985) for name, res in all_inspections(result): paginator.add_line(f"{name:16.16} :: {res}") interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) send(await interface.send_to(ctx)) finally: scope.clear_intersection(arg_dict)
async def python(event): message = event.message async with utils.ReplExceptionCatcher(message): async for send, x in AsyncSender( AsyncCodeExecutor(event.command_text, arg_dict=dict( event=event, telethon=telethon, tl=tl, message=message, _=event.client.last_python_result, ))): if x is not None: event.client.last_python_result = x if not isinstance(x, str): x = repr(x) if not x.strip(): # represent nothingness, without actually sending nothing x = '\N{zero width space}' message = await message.reply(x) send(message) await event.reply('✅')
async def jsk_python_inspect(self, ctx: commands.Context, *, argument: codeblock_converter): """ Evaluation of Python code with inspect information. """ arg_dict = get_var_dict_from_ctx(ctx, Flags.SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(argument.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): self.last_result = result header = repr(result).replace("``", "`\u200b`").replace( self.bot.http.token, "[token omitted]") if len(header) > 485: header = header[0:482] + "..." lines = [f"=== {header} ===", ""] for name, res in all_inspections(result): lines.append(f"{name:16.16} :: {res}") docstring = (inspect.getdoc(result) or '').strip() if docstring: lines.append(f"\n=== Help ===\n\n{docstring}") text = "\n".join(lines) if use_file_check(ctx, len( text)): # File "full content" preview limit send(await ctx.send(file=discord.File( filename="inspection.prolog", fp=io.BytesIO(text.encode('utf-8'))))) else: paginator = WrappedPaginator(prefix="```prolog", max_size=1985) paginator.add_line(text) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) send(await interface.send_to(ctx)) finally: scope.clear_intersection(arg_dict)
async def traverse(self, func): std = io.StringIO() with contextlib.redirect_stdout(std): if inspect.isasyncgenfunction(func): async for send, result in AsyncSender(func(*self.args)): if content := std.getvalue(): std.seek(0) std.truncate(0) yield content send((yield result)) else:
async def traverse(self, func): """ Traverses an async function or generator, yielding each result. This function is private. The class should be used as an iterator instead of using this method. """ if inspect.isasyncgenfunction(func): async for send, result in AsyncSender(func(*self.args)): send((yield result)) else: yield await func(*self.args)
async def jsk_python_inspect(self, ctx: commands.Context, *, argument: codeblock_converter): # pylint: disable=too-many-locals """ Evaluation of Python code with inspect information. """ arg_dict = get_var_dict_from_ctx(ctx, SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(argument.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): self.last_result = result header = repr(result).replace("``", "`\u200b`").replace(self.bot.http.token, "[token omitted]") if len(header) > 485: header = header[0:482] + "..." lines = [f"=== {header} ===", ""] for name, res in all_inspections(result): lines.append(f"{name:16.16} :: {res}") text = "\n".join(lines) # Guild's advertised limit minus 1KiB for the HTTP content filesize_threshold = (ctx.guild.filesize_limit if ctx.guild else 8 * 1024 * 1024) - 1024 if len(text) < filesize_threshold: send(await ctx.send(file=discord.File( filename="inspection.prolog", fp=io.BytesIO(text.encode('utf-8')) ))) else: paginator = WrappedPaginator(prefix="```prolog", max_size=1985) paginator.add_line(text) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) send(await interface.send_to(ctx)) finally: scope.clear_intersection(arg_dict)
async def traverse(self, func): """ Traverses an async function or generator, yielding each result. This function is private. The class should be used as an iterator instead of using this method. """ try: if inspect.isasyncgenfunction(func): async for send, result in AsyncSender(func(*self.args)): send((yield result)) else: yield await func(*self.args) except Exception: # Falsely populate the linecache to make the REPL line appear in tracebacks linecache.cache['<repl>'] = ( len(self.source), # Source length None, # Time modified (None bypasses expunge) [line + '\n' for line in self.source.splitlines()], # Line list '<repl>' # 'True' filename ) raise
async def jsk_python(self, ctx: commands.Context, *, argument: codeblock_converter): """ Direct evaluation of Python code. """ arg_dict = get_var_dict_from_ctx(ctx, SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(argument.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): if result is None: continue self.last_result = result if isinstance(result, discord.File): send(await ctx.send(file=result)) elif isinstance(result, discord.Embed): send(await ctx.send(embed=result)) elif isinstance(result, PaginatorInterface): send(await result.send_to(ctx)) else: if not isinstance(result, str): # repr all non-strings result = repr(result) # Guild's advertised limit minus 1KiB for the HTTP content filesize_threshold = (ctx.guild.filesize_limit if ctx.guild else 8 * 1024 * 1024) - 1024 if len(result) <= 2000: if result.strip() == '': result = "\u200b" send(await ctx.send(result.replace(self.bot.http.token, "[token omitted]"))) elif len(result) < filesize_threshold: # Discord's desktop and web client now supports an interactive file content # display for files encoded in UTF-8. # Since this avoids escape issues and is more intuitive than pagination for # long results, it will now be prioritized over PaginatorInterface if the # resultant content is below the filesize threshold send(await ctx.send(file=discord.File( filename="output.py", fp=io.BytesIO(result.encode('utf-8')) ))) else: # inconsistency here, results get wrapped in codeblocks when they are too large # but don't if they're not. probably not that bad, but noting for later review paginator = WrappedPaginator(prefix='```py', suffix='```', max_size=1985) paginator.add_line(result) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) send(await interface.send_to(ctx)) finally: scope.clear_intersection(arg_dict)
async def _dev_python(self, ctx, *, code: codeblock_converter): ''' Evaluate python code ''' arg_dict = get_var_dict_from_ctx(ctx, "") arg_dict["_"] = self.last_result scope = self._scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(code.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): if result is None: continue self.last_result = result if isinstance(result, discord.File): send(await ctx.reply(file=result)) elif isinstance(result, discord.Embed): send(await ctx.reply(embed=result)) elif isinstance(result, PaginatorInterface): send(await result.send_to(ctx)) else: if not isinstance(result, str): result = repr(result) if len(result) > 2000: result = result.replace("`", "`\u200b") width = 2000 pages = [ result[i:i + width] for i in range(0, len(result), width) ] for page in pages: embed = discord.Embed( description=f"```py\n{page}```", colour=discord.Colour.teal()) pages[pages.index(page)] = embed embedpaginator = paginator( ctx, remove_reactions=True) embedpaginator.add_reaction( "\U000023ea", "first") embedpaginator.add_reaction( "\U000025c0", "back") embedpaginator.add_reaction( "\U0001f5d1", "delete") embedpaginator.add_reaction( "\U000025b6", "next") embedpaginator.add_reaction( "\U000023e9", "last") send(await embedpaginator.send(pages)) else: if result.strip() == '': result = "\u200b" embed = discord.Embed( description= f"```py\n{result.replace(self.bot.http.token, '[token]')}```", colour=discord.Colour.teal()) send(await ctx.reply(embed=embed, mention_author=False)) finally: scope.clear_intersection(arg_dict)