• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thomwiggers / onebot / 20856345966

09 Jan 2026 03:13PM UTC coverage: 68.234% (-0.8%) from 69.064%
20856345966

Pull #213

github

thomwiggers
feat: handle newlines in stderr in python plugin
Pull Request #213: feat: refactor python-sandbox to standalone server

2 of 31 new or added lines in 1 file covered. (6.45%)

827 of 1212 relevant lines covered (68.23%)

3.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

23.26
/onebot/plugins/python.py
1
"""
2
=====================================================
3
:mod:`onebot.plugins.python` Run python commands in docker
4
=====================================================
5

6
Allow to run Python commands
7

8
Config:
9
=======
10

11
.. code-block: ini
12

13
    [bot]
14
    includes=
15
      onebot.plugins.python
16
"""
17

18
from typing import Self
5✔
19
import irc3
5✔
20
from irc3.plugins.command import command
5✔
21
import requests
5✔
22
import os
5✔
23

24

25
@irc3.plugin
5✔
26
class PythonPlugin:
5✔
27
    """Execute commands after having connected"""
28

29
    def __init__(self, bot):
5✔
30
        self.bot = bot
×
31
        self.log = bot.log.getChild(__name__)
×
NEW
32
        self.sandbox_url = os.environ.get("PYTHON_SANDBOX_URL", "http://localhost:8080")
×
33

34
    @command(use_shlex=False)
5✔
35
    def py(self, _mask, _target, args):
5✔
36
        """Execute a command in a Python 3 interpreter
37

38
        %%py <command>...
39
        """
40
        cmd = " ".join(args["<command>"])
×
41
        self.log.debug("Command: '%s'", cmd)
×
NEW
42
        try:
×
NEW
43
            response = requests.post(
×
44
                self.sandbox_url,
45
                json={"code": cmd},
46
                timeout=10,
47
            )
NEW
48
            response.raise_for_status()
×
NEW
49
            data = response.json()
×
NEW
50
            stdout = data.get("stdout", "")
×
NEW
51
            stderr = data.get("stderr", "")
×
52

NEW
53
            if stderr:
×
NEW
54
                lines = stderr.split("\n")
×
NEW
55
                if len(lines) > 2:
×
NEW
56
                    yield f"Error: {lines[0][:200]}"
×
NEW
57
                    yield "Too many error lines returned?"
×
58
                else:
NEW
59
                    for line in lines:
×
NEW
60
                        yield f"Error: {line[:200]}"
×
NEW
61
                return
×
62

NEW
63
            if not stdout:
×
NEW
64
                yield "No output."
×
NEW
65
                return
×
66

NEW
67
            lines = stdout.split("\n")
×
NEW
68
            if len(lines) > 2:
×
NEW
69
                self.log.warning("Too many lines for '%s'", cmd)
×
NEW
70
                self.log.info("Output: %r", lines)
×
NEW
71
                yield "Too many lines returned?"
×
NEW
72
                return
×
73

NEW
74
            for line in lines:
×
NEW
75
                yield line[:200]
×
76

NEW
77
        except requests.exceptions.RequestException as e:
×
NEW
78
            self.log.error("Failed to connect to sandbox: %s", e)
×
NEW
79
            yield "Error: Could not connect to Python sandbox."
×
80

81
    @classmethod
82
    def reload(cls, old: Self) -> Self:  # pragma: no cover
83
        return cls(old.bot)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc