示例#1
0
文件: bot.py 项目: ioistired/AoC-Bot
async def python_command(event):
    reply_to = event.message
    dest = await event.get_input_chat()
    async for x in AsyncCodeExecutor(event.pattern_match.group(1),
                                     arg_dict=dict(event=event)):
        if type(x) is not str:
            x = repr(x)
        if x == '':
            x = repr(x)

        reply_to = await event.client.send_message(dest, x, reply_to=reply_to)

    await event.reply('✅')
示例#2
0
async def test_executor_builtins(scope):
    codeblock = inspect.cleandoc("""
    def ensure_builtins():
        return ValueError
    """)

    return_data = []
    async for result in AsyncCodeExecutor(codeblock, scope):
        return_data.append(result)

    assert len(return_data) == 1
    assert return_data[0] is None

    assert 'ensure_builtins' in scope.globals, "Checking function remains defined"
    assert callable(scope.globals['ensure_builtins']), "Checking defined is callable"
    assert scope.globals['ensure_builtins']() == ValueError, "Checking defined return consistent"
示例#3
0
    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)
示例#4
0
    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)
示例#5
0
    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)
示例#6
0
    async def jsk_python(self, ctx: commands.Context, *, argument: CodeblockConverter):
        """
        Direct evaluation of Python code.
        """

        arg_dict = get_var_dict_from_ctx(ctx)

        scope = self.scope

        scope.clean()
        arg_dict["_"] = self.last_result

        async with ReplResponseReactor(ctx.message):
            with self.submit(ctx):
                async for result in AsyncCodeExecutor(
                    argument.content, scope, arg_dict=arg_dict
                ):
                    if result is None:
                        continue

                    self.last_result = result

                    if isinstance(result, discord.File):
                        await ctx.send(file=result)
                    elif isinstance(result, discord.Embed):
                        await ctx.send(embed=result)
                    else:
                        if not isinstance(result, str):
                            # repr all non-strings
                            result = repr(result)

                        if len(result) > 2000:
                            result = result[0:1997] + "..."
                        elif result.strip() == "":
                            result = "\u200b"

                        await ctx.send(
                            result.replace(self.bot.http.token, "[token omitted]")
                        )
示例#7
0
    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)
示例#8
0
    async def internal_test(self):
        scope = Scope()

        return_data = []
        async for result in AsyncCodeExecutor('3 + 4', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking eval produces single result")
        self.assertEqual(return_data[0],
                         7,
                         msg="Checking eval result is consistent")

        return_data = []
        async for result in AsyncCodeExecutor('return 3 + 9', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking manual return produces single result")
        self.assertEqual(return_data[0],
                         12,
                         msg="Checking return result is consistent")

        return_data = []
        async for result in AsyncCodeExecutor('b = 12 + 82', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking that assignment returns")
        self.assertIsNone(return_data[0],
                          msg="Checking assignment returns None")

        return_data = []
        async for result in AsyncCodeExecutor('b', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking variable eval returns")
        self.assertEqual(return_data[0],
                         94,
                         msg="Checking retained variable consistent")

        scope.update_globals({'d': 41})

        return_data = []
        async for result in AsyncCodeExecutor('c = d + 7; c', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking multi-expression implicitly returns")
        self.assertEqual(return_data[0],
                         48,
                         msg="Checking last expression return is consistent")

        return_data = []
        async for result in AsyncCodeExecutor('yield 30; yield 40', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         2,
                         msg="Checking two yields returns two results")
        self.assertEqual(return_data[0],
                         30,
                         msg="Checking first yield consistency")
        self.assertEqual(return_data[1],
                         40,
                         msg="Checking second yield consistency")

        return_data = []
        async for result in AsyncCodeExecutor('yield 60; 70', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         2,
                         msg="Checking multi-statement implicitly yields")
        self.assertEqual(return_data[0],
                         60,
                         msg="Checking explicit yield consistent")
        self.assertEqual(return_data[1],
                         70,
                         msg="Checking implicit yield consistent")

        return_data = []
        async for result in AsyncCodeExecutor('90; 100', scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking multi-statement implicitly returns")
        self.assertEqual(return_data[0],
                         100,
                         msg="Checking implicit return consistent")

        arg_dict = {'_cool_data': 45, '_not_so_cool': 400}
        return_data = []
        async for result in AsyncCodeExecutor('_cool_data + _not_so_cool',
                                              scope,
                                              arg_dict=arg_dict):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking arg dictionary expression returned")
        self.assertEqual(return_data[0],
                         445,
                         msg="Checking arg dictionary expression consistent")

        scope.clean()

        hit_exception = False
        try:
            async for result in AsyncCodeExecutor('_cool_data', scope):
                pass
        except NameError:
            hit_exception = True

        self.assertTrue(hit_exception, msg="Checking private locals removed")

        scope2 = Scope()
        scope2.update(scope)

        self.assertEqual(scope.globals,
                         scope2.globals,
                         msg="Checking scope globals copied")
        self.assertEqual(scope.locals,
                         scope2.locals,
                         msg="Checking scope locals copied")

        scope.update_locals({'e': 7})

        self.assertIn('e', scope.locals, msg="Checking scope locals updated")
        self.assertNotIn('e',
                         scope2.locals,
                         msg="Checking scope clone locals not updated")

        scope.clean()

        codeblock = inspect.cleandoc("""
        def ensure_builtins():
            return ValueError
        """)

        async for result in AsyncCodeExecutor(codeblock, scope):
            pass

        scope.clean()

        self.assertIn('ensure_builtins',
                      scope.globals,
                      msg="Checking function remains defined")
        self.assertTrue(callable(scope.globals['ensure_builtins']),
                        msg="Checking defined is callable")
        self.assertEqual(scope.globals['ensure_builtins'](),
                         ValueError,
                         msg="Checking defined retuurn consistent")

        if sys.version_info >= (3, 7):
            codeblock = inspect.cleandoc("""
            eval('''
            3 + 4
            ''')
            """)

            return_data = []
            async for result in AsyncCodeExecutor(codeblock, scope):
                return_data.append(result)

            self.assertEqual(len(return_data),
                             1,
                             msg="Checking multi-line docstring eval returns")
            self.assertEqual(return_data[0],
                             7,
                             msg="Checking eval return consistent")

            scope.clean()

        scope.update_globals({'add_numbers': self.add_numbers})

        return_data = []
        async for result in AsyncCodeExecutor("await add_numbers(10, 12)",
                                              scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking await returns result")
        self.assertEqual(return_data[0],
                         22,
                         msg="Checking await result consistent")

        return_data = []
        async for result in AsyncCodeExecutor("", scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking empty eval returns")
        self.assertIsNone(return_data[0],
                          msg="Checking empty eval returns None")

        return_data = []
        async for result in AsyncCodeExecutor("# this is a comment", scope):
            return_data.append(result)

        self.assertEqual(len(return_data),
                         1,
                         msg="Checking eval of only comment returns")
        self.assertIsNone(return_data[0],
                          msg="Checking eval of only comment returns None")