Add eval/exec to RPC

This commit is contained in:
dpedu 2015-08-08 15:04:45 -07:00
parent c2e2199b02
commit 5a511944bd
4 changed files with 605 additions and 510 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ build
pyircbot.egg-info
dev
docs/builder/build.sh
examples/config.test.json

View File

@ -47,7 +47,7 @@ We can retrieve an arbitrary property from a module:
[True, {'apikey': 'deadbeefcafe', 'defaultUnit': 'f'}]
>>>
Or run a method in a module, passing args:
Run a method in a module, passing args:
.. code-block:: python
@ -55,6 +55,71 @@ Or run a method in a module, passing args:
[True, {'definition': "Loyal, unlike its predecessor", 'word': 'rhobot2', 'by': 'xMopxShell'}]
>>>
Or simply pass a string to eval() or exec() to do anything. In this case,
retrieving a full stack trace of the bot, which is useful during module
development:
.. code-block:: python
>>> print( rpc.eval("self.bot.irc.trace()")[1] )
*** STACKTRACE - START ***
# ThreadID: 140289192748800
File: "/usr/lib/python3.4/threading.py", line 888, in _bootstrap
self._bootstrap_inner()
File: "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
self.run()
File: "/usr/lib/python3.4/threading.py", line 1184, in run
self.finished.wait(self.interval)
File: "/usr/lib/python3.4/threading.py", line 552, in wait
signaled = self._cond.wait(timeout)
File: "/usr/lib/python3.4/threading.py", line 293, in wait
gotit = waiter.acquire(True, timeout)
# ThreadID: 140289297204992
File: "/usr/lib/python3.4/threading.py", line 888, in _bootstrap
self._bootstrap_inner()
File: "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
self.run()
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/rpc.py", line 51, in run
self.server.serve()
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/jsonrpc.py", line 1110, in serve
self.__transport.serve( self.handle, n )
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/jsonrpc.py", line 851, in serve
result = handler(data)
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/jsonrpc.py", line 1086, in handle
result = self.funcs[method]( *params )
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/rpc.py", line 167, in eval
return (True, eval(code))
File: "<string>", line 1, in <module>
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/irccore.py", line 288, in trace
for filename, lineno, name, line in traceback.extract_stack(stack):
# ThreadID: 140289333405504
File: "/usr/local/bin/pyircbot", line 5, in <module>
pkg_resources.run_script('pyircbot==4.0.0-r02', 'pyircbot')
File: "/usr/lib/python3/dist-packages/pkg_resources.py", line 528, in run_script
self.require(requires)[0].run_script(script_name, ns)
File: "/usr/lib/python3/dist-packages/pkg_resources.py", line 1394, in run_script
execfile(script_filename, namespace, namespace)
File: "/usr/lib/python3/dist-packages/pkg_resources.py", line 55, in execfile
exec(compile(open(fn).read(), fn, 'exec'), globs, locs)
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/EGG-INFO/scripts/pyircbot", line 32, in <module>
bot.loop()
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/pyircbot.py", line 68, in loop
self.irc.loop()
File: "/usr/local/lib/python3.4/dist-packages/pyircbot-4.0.0_r02-py3.4.egg/pyircbot/irccore.py", line 56, in loop
asyncore.loop(map=self.asynmap)
File: "/usr/lib/python3.4/asyncore.py", line 208, in loop
poll_fun(timeout, map)
File: "/usr/lib/python3.4/asyncore.py", line 145, in poll
r, w, e = select.select(r, w, e, timeout)
*** STACKTRACE - END ***
>>>
Careful, you can probably crash the bot by tweaking the wrong things. Only
basic types can be passed over the RPC connection. Trying to access anything
extra results in an error:

View File

@ -11,6 +11,7 @@ import asynchat
import asyncore
import logging
import traceback
import sys
from socket import SHUT_RDWR
try:
@ -279,7 +280,19 @@ class IRCCore(asynchat.async_chat):
@staticmethod
def trace():
"""Return the stack trace of the bot as a string"""
return traceback.format_exc()
result = ""
result += "\n*** STACKTRACE - START ***\n"
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# ThreadID: %s" % threadId)
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
for line in code:
result += line + "\n"
result += "\n*** STACKTRACE - END ***\n"
return result
" Data Methods "
def get_nick(self):

View File

@ -41,6 +41,8 @@ class BotRPC(Thread):
self.server.register_function( self.setPluginVar )
self.server.register_function( self.getPluginVar )
self.server.register_function( self.quit )
self.server.register_function( self.eval )
self.server.register_function( self.exec )
self.start()
@ -157,6 +159,20 @@ class BotRPC(Thread):
setattr(plugin, moduleVarName, value)
return (True, "Var set")
def eval(self, code):
"""Execute arbitrary python code on the bot
:param code: Python code to pass to eval
:type code: str"""
return (True, eval(code))
def exec(self, code):
"""Execute arbitrary python code on the bot
:param code: Python code to pass to exec
:type code: str"""
return (True, exec(code))
def quit(self, message):
"""Tell the bot to quit IRC and exit