Add the /edit swtich to the @py command

This commit is contained in:
Vincent Le Goff 2017-02-11 12:43:58 -08:00 committed by Griatch
parent 36b268ca81
commit 1697263fd7
2 changed files with 108 additions and 54 deletions

View file

@ -19,6 +19,7 @@ from evennia.scripts.models import ScriptDB
from evennia.objects.models import ObjectDB
from evennia.players.models import PlayerDB
from evennia.utils import logger, utils, gametime, create, prettytable
from evennia.utils.eveditor import EvEditor
from evennia.utils.evtable import EvTable
from evennia.utils.utils import crop, class_from_module
@ -122,19 +123,101 @@ class CmdShutdown(COMMAND_DEFAULT_CLASS):
SESSIONS.portal_shutdown()
def _py_load(caller):
return ""
def _py_save(caller, buf):
"""
Execute the buffer.
"""
caller.msg("Executing the entered code...")
_run_snippet(caller, buf, mode="exec", show_input=False)
return True
def _py_quit(caller):
caller.msg("Exited the code editor.")
def _run_snippet(caller, pycode, mode="eval", m_time=False,
show_input=True):
"""
Run code and try to display information to the caller.
Args:
caller (Object): the caller.
pycode (str): the Python code to run.
m_time (bool, optional): should we measure the time of execution?
show_input (bookl, optional): should we display the input?
"""
# Try to retrieve the session
session = caller
if hasattr(caller, "sessions"):
session = caller.sessions.get()[0]
# import useful variables
import evennia
available_vars = {
'self': caller,
'me': caller,
'here': getattr(caller, "location", None),
'evennia': evennia,
'ev': evennia,
'inherits_from': utils.inherits_from,
}
if show_input:
try:
caller.msg(">>> %s" % pycode, session=session,
options={"raw":True})
except TypeError:
caller.msg(">>> %s" % pycode, options={"raw":True})
try:
try:
pycode_compiled = compile(pycode, "", mode)
except Exception:
mode = "exec"
pycode_compiled = compile(pycode, "", mode)
duration = ""
if m_time:
t0 = time.time()
ret = eval(pycode_compiled, {}, available_vars)
t1 = time.time()
duration = " (runtime ~ %.4f ms)" % ((t1 - t0) * 1000)
else:
ret = eval(pycode_compiled, {}, available_vars)
if mode == "eval":
ret = "%s%s" % (str(ret), duration)
else:
ret = " Done (use self.msg() if you want to catch output)%s" % duration
except Exception:
errlist = traceback.format_exc().split('\n')
if len(errlist) > 4:
errlist = errlist[4:]
ret = "\n".join("%s" % line for line in errlist if line)
try:
caller.msg(ret, session=session, options={"raw":True})
except TypeError:
caller.msg(ret, options={"raw":True})
class CmdPy(COMMAND_DEFAULT_CLASS):
"""
execute a snippet of python code
Usage:
@py <cmd>
@py/edit
Switch:
time - output an approximate execution time for <cmd>
edit - open a code editor to enter several lines of code
Separate multiple commands by ';'. A few variables are made
available for convenience in order to offer access to the system
(you can import more at execution time).
Separate multiple commands by ';' or open the editor using the
/edit switch. A few variables are made available for convenience
in order to offer access to the system (you can import more at
execution time).
Available variables in @py environment:
self, me : caller
@ -160,58 +243,18 @@ class CmdPy(COMMAND_DEFAULT_CLASS):
caller = self.caller
pycode = self.args
if "edit" in self.switches:
EvEditor(self.caller, loadfunc=_py_load, savefunc=_py_save,
quitfunc=_py_quit, key="PyEditor", persistent=True,
code=True)
return
if not pycode:
string = "Usage: @py <code>"
self.msg(string)
return
# check if caller is a player
# import useful variables
import evennia
available_vars = {'self': caller,
'me': caller,
'here': hasattr(caller, "location") and caller.location or None,
'evennia': evennia,
'ev': evennia,
'inherits_from': utils.inherits_from}
try:
self.msg(">>> %s" % pycode, session=self.session, options={"raw":True})
except TypeError:
self.msg(">>> %s" % pycode, options={"raw":True})
mode = "eval"
try:
try:
pycode_compiled = compile(pycode, "", mode)
except Exception:
mode = "exec"
pycode_compiled = compile(pycode, "", mode)
duration = ""
if "time" in self.switches:
t0 = time.time()
ret = eval(pycode_compiled, {}, available_vars)
t1 = time.time()
duration = " (runtime ~ %.4f ms)" % ((t1 - t0) * 1000)
else:
ret = eval(pycode_compiled, {}, available_vars)
if mode == "eval":
ret = "%s%s" % (str(ret), duration)
else:
ret = " Done (use self.msg() if you want to catch output)%s" % duration
except Exception:
errlist = traceback.format_exc().split('\n')
if len(errlist) > 4:
errlist = errlist[4:]
ret = "\n".join("%s" % line for line in errlist if line)
try:
self.msg(ret, session=self.session, options={"raw":True})
except TypeError:
self.msg(ret, options={"raw":True})
_run_snippet(caller, self.args, m_time="time" in self.switches)
# helper function. Kept outside so it can be imported and run
# by other commands.

View file

@ -375,7 +375,7 @@ class CmdLineInput(CmdEditorBase):
buf = editor.get_buffer()
# add a line of text to buffer
line = self.raw_string
line = self.raw_string.strip("\r\n")
if not editor._code:
if not buf:
buf = line
@ -396,8 +396,12 @@ class CmdLineInput(CmdEditorBase):
cline = len(self.editor.get_buffer().split('\n'))
if editor._code:
# display the current level of identation
self.caller.msg("{b%02i|{n ({g%d{n) %s" % (
cline, editor._indent, line))
indent = editor._indent
if indent < 0:
indent = "off"
self.caller.msg("{b%02i|{n ({g%s{n) %s" % (
cline, indent, line))
else:
self.caller.msg("{b%02i|{n %s" % (cline, self.args))
@ -503,6 +507,13 @@ class CmdEditorGroup(CmdEditorBase):
elif cmd == ":DD":
# clear buffer
editor.update_buffer("")
# Reset indentation level to 0
if editor._code:
if editor._indent >= 0:
editor._indent = 0
if editor._persistent:
caller.attributes.add("_eveditor_indent", 0)
caller.msg("Cleared %i lines from buffer." % self.nlines)
elif cmd == ":y":
# :y <l> - yank line(s) to copy buffer
@ -949,7 +960,7 @@ class EvEditor(object):
"""
string = self._sep * _DEFAULT_WIDTH + _HELP_TEXT
if self.code:
if self._code:
string += _HELP_CODE
string += _HELP_LEGEND + self._sep * _DEFAULT_WIDTH
self._caller.msg(string)