Skip to content

Commit 8aa5877

Browse files
fix(ieval): catch SyntaxErrors with eval and try with exec
1 parent 73948bc commit 8aa5877

File tree

3 files changed

+41
-24
lines changed

3 files changed

+41
-24
lines changed

monty/exts/core/admin.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def create_file_obj(
7676

7777
log = get_logger(__name__)
7878

79-
Executor = Union[exec, eval]
79+
Executor = t.Literal["exec", "eval"]
8080

8181
MESSAGE_LIMIT = 2000
8282

@@ -120,22 +120,22 @@ async def cog_check(self, ctx: commands.Context) -> bool:
120120
def get_syntax_error(self, e: SyntaxError) -> str:
121121
"""If there's a syntax error in the exception, get some text from it."""
122122
if e.text is None:
123-
return f"```py\n{e.__class__.__name__}: {e}\n```"
124-
return f"```py\n{e.text}{'^':>{e.offset}}\n{e.__class__.__name__}: {e}```"
123+
return f"```ansi\n{e.__class__.__name__}: {e}\n```"
124+
return f"```ansi\n{e.text}{'^':>{e.offset}}\n{e.__class__.__name__}: {e}\n```"
125125

126126
@staticmethod
127127
def _runwith(code: str) -> Executor:
128128
"""Determine which method to run the code."""
129129
code = code.strip()
130130
if ";" in code:
131-
return exec
131+
return "exec"
132132
if "\n" in code:
133-
return exec
133+
return "exec"
134134
parsed_code = ast.parse(code)
135135
for node in ast.iter_fields(parsed_code):
136136
if isinstance(node, ast.Assign):
137-
return exec
138-
return eval
137+
return "exec"
138+
return "eval"
139139

140140
async def _send_stdout(
141141
self,
@@ -166,8 +166,8 @@ async def _send_stdout(
166166
# unless, for some reason, it has a ``` in it
167167
error_file: disnake.File = None
168168
total_len = 0
169-
fmt_resp: str = "```py\n{0}```"
170-
fmt_err: str = "\nAn error occured. Unfortunate.```py\n{0}```"
169+
fmt_resp: str = "```ansi\n{0}```"
170+
fmt_err: str = "\nAn error occured. Unfortunate.```ansi\n{0}```"
171171
out = ""
172172
files = []
173173

@@ -185,12 +185,11 @@ async def _send_stdout(
185185
error_file = True
186186

187187
if total_len > MESSAGE_LIMIT or resp_file:
188-
log.debug("rats we gotta upload as a file")
188+
log.debug("Evaluation response is %i characters long, sending as file.", total_len)
189189

190190
resp_file: disnake.File = create_file_obj(resp, ext="py")
191191
else:
192-
# good job, not a file
193-
log.debug("sending response as plaintext")
192+
log.debug("sending evaluation response as plaintext")
194193
out += fmt_resp.format(resp) if resp is not None else ""
195194
out += fmt_err.format(error) if error is not None else ""
196195

@@ -239,21 +238,34 @@ async def _eval(
239238
result = None
240239
error = None
241240
try:
242-
with redirect_stdout(stdout):
243-
runwith = self._runwith(code)
244-
log.trace(runwith.__name__)
241+
runwith = self._runwith(code)
242+
log.trace(runwith)
243+
try:
244+
co_code = compile(
245+
code,
246+
"<int eval>",
247+
runwith,
248+
flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT,
249+
)
250+
except SyntaxError:
251+
runwith = "exec"
252+
# try using exec if that fails
245253
co_code = compile(
246254
code,
247255
"<int eval>",
248-
runwith.__name__,
256+
runwith,
249257
flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT,
250258
)
251259

252-
if inspect.CO_COROUTINE & co_code.co_flags == inspect.CO_COROUTINE:
260+
with redirect_stdout(stdout):
261+
if inspect.CO_COROUTINE & co_code.co_flags:
253262
awaitable = FunctionType(co_code, env)
254263
result = await awaitable()
255264
else:
256-
result = runwith(co_code, env)
265+
if runwith == "eval":
266+
result = eval(co_code, env) # noqa: S307
267+
else:
268+
result = exec(co_code, env) # noqa: S102
257269
except Exception:
258270
exc_type, exc_value, exc_traceback = sys.exc_info()
259271
error = traceback.format_exception(exc_type, exc_value, exc_traceback)
@@ -369,14 +381,14 @@ async def _repl(self, ctx: commands.Context, variables: dict, check: t.Any) -> N
369381
result = await result
370382
except Exception:
371383
value = stdout.getvalue()
372-
fmt = f"```py\n{value}{traceback.format_exc()}\n```"
384+
fmt = f"```ansi\n{value}{traceback.format_exc()}\n```"
373385
else:
374386
value = stdout.getvalue()
375387
if result is not None:
376-
fmt = f"```py\n{value}{result}\n```"
388+
fmt = f"```ansi\n{value}{result}\n```"
377389
variables["_"] = result
378390
elif value:
379-
fmt = f"```py\n{value}\n```"
391+
fmt = f"```ansi\n{value}\n```"
380392
try:
381393
if fmt is not None:
382394
if len(fmt) > MESSAGE_LIMIT:

monty/exts/core/delete.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33

44
from monty.bot import Monty
55
from monty.log import get_logger
6+
from monty.metadata import ExtMetadata
67
from monty.utils.messages import DELETE_ID_V2
78

89

10+
EXT_METADATA = ExtMetadata(core=True)
11+
912
VIEW_DELETE_ID_V1 = "wait_for_deletion_interaction_trash"
1013

1114
logger = get_logger(__name__)

monty/log.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515

1616

1717
try:
18+
from rich.console import Console
1819
from rich.logging import RichHandler
1920
except ImportError:
2021
RichHandler = None
22+
Console = None
2123

2224
TRACE = 5
2325

@@ -86,12 +88,12 @@ def setup() -> None:
8688
file_handler.setFormatter(log_format)
8789
root_logger.addHandler(file_handler)
8890

89-
if RichHandler is not None:
90-
rich_handler = RichHandler(rich_tracebacks=True)
91+
if RichHandler and Console:
92+
rich_handler = RichHandler(rich_tracebacks=True, console=Console(stderr=True))
9193
# rich_handler.setFormatter(log_format)
9294
root_logger.addHandler(rich_handler)
9395
else:
94-
console_handler = logging.StreamHandler(sys.stdout)
96+
console_handler = logging.StreamHandler(sys.stderr)
9597
console_handler.setFormatter(log_format)
9698
root_logger.addHandler(console_handler)
9799

0 commit comments

Comments
 (0)