1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 16:51:11 +03:00
awx/tools/rdb.py
2017-12-01 12:16:44 -05:00

243 lines
6.6 KiB
Python

import rlcompleter
try:
import readline
except ImportError:
print("Module readline not available.")
else:
if 'libedit' in readline.__doc__:
readline.parse_and_bind("bind ^I rl_complete")
else:
readline.parse_and_bind("tab: complete")
import sys
from celery.contrib.rdb import Rdb
import cmd
import contextlib
import logging
import os
import pprint
import re
import select
import socket
import threading
from cStringIO import StringIO
from Queue import Queue, Empty
from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import Terminal256Formatter
logger = logging.getLogger('awx')
@contextlib.contextmanager
def style(im_self, filepart=None, lexer=None):
lexer = PythonLexer
old_stdout = im_self.stdout
buff = StringIO()
im_self.stdout = buff
yield
value = buff.getvalue()
context = len(value.splitlines())
file_cache = {}
if filepart:
filepath, lineno = filepart
if filepath not in file_cache:
with open(filepath, 'r') as source:
file_cache[filepath] = source.readlines()
value = ''.join(file_cache[filepath][:lineno - 1]) + value
formatter = Terminal256Formatter(style='friendly')
value = highlight(value, lexer(), formatter)
# Properly format line numbers when they show up in multi-line strings
strcolor, _ = formatter.style_string['Token.Literal.String']
intcolor, _ = formatter.style_string['Token.Literal.Number.Integer']
value = re.sub(
r'%s([0-9]+)' % re.escape(strcolor),
lambda match: intcolor + match.group(1) + strcolor,
value,
)
# Highlight the "current" line in yellow for visibility
lineno = im_self.curframe.f_lineno
value = re.sub(
'(?<!\()%s%s[^\>]+>[^\[]+\[39m([^\x1b]+)[^m]+m([^\n]+)' % (re.escape(intcolor), lineno),
lambda match: ''.join([
str(lineno),
' ->',
'\x1b[93m',
match.group(1),
re.sub('\x1b[^m]+m', '', match.group(2)),
'\x1b[0m'
]),
value
)
if filepart:
_, first = filepart
value = '\n'.join(value.splitlines()[-context:]) + '\n'
if value.strip():
old_stdout.write(value)
im_self.stdout = old_stdout
class CustomPdb(Rdb):
def cmdloop(self):
self.do_list(tuple())
return cmd.Cmd.cmdloop(self)
def do_list(self, args):
lines = 60
context = (lines - 2) / 2
if not args:
first = max(1, self.curframe.f_lineno - context)
last = first + context * 2 - 1
args = "(%s, %s)" % (first, last)
self.lineno = None
with style(self, (
self.curframe.f_code.co_filename, self.curframe.f_lineno - context)
):
return Rdb.do_list(self, args)
do_l = do_list
def format_stack_entry(self, *args, **kwargs):
entry = Rdb.format_stack_entry(self, *args, **kwargs)
return '\n'.join(
filter(lambda x: not x.startswith('->'), entry.splitlines())
)
def print_stack_entry(self, *args, **kwargs):
with style(self):
return Rdb.print_stack_entry(self, *args, **kwargs)
def set_next(self, curframe):
os.system('clear')
Rdb.set_next(self, curframe)
def set_return(self, arg):
os.system('clear')
Rdb.set_return(self, arg)
def set_step(self):
os.system('clear')
Rdb.set_step(self)
def default(self, line):
with style(self):
return Rdb.default(self, line)
def parseline(self, line):
line = line.strip()
match = re.search('^([0-9]+)([a-zA-Z]+)', line)
if match:
times, command = match.group(1), match.group(2)
line = command
self.cmdqueue.extend(list(command * (int(times) - 1)))
if line == '?':
line = 'dir()'
elif line.endswith('??'):
line = "import inspect; print ''.join(inspect.getsourcelines(%s)[0][:25])" % line[:-2]
elif line.endswith('?'):
line = 'dir(%s)' % line[:-1]
return cmd.Cmd.parseline(self, line)
def displayhook(self, obj):
if obj is not None and not isinstance(obj, list):
return pprint.pprint(obj)
return Rdb.displayhook(self, obj)
def get_avail_port(self, *args, **kwargs):
try:
socket.gethostbyname('docker.for.mac.localhost')
host = 'docker.for.mac.localhost'
except Exception:
host = os.popen('ip route').read().split(' ')[2]
sock, port = Rdb.get_avail_port(self, *args, **kwargs)
socket.socket(socket.AF_INET, socket.SOCK_DGRAM).sendto(
str(port), (host, 6899)
)
return (sock, port)
def say(self, m):
logger.warning(m)
CustomPdb.complete = rlcompleter.Completer(locals()).complete
def set_trace():
return CustomPdb().set_trace(sys._getframe().f_back)
def listen():
queue = Queue()
def _consume(queue):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 6899))
print 'listening for rdb notifications on :6899...'
while True:
r, w, x = select.select([sock], [], [])
for i in r:
data = i.recv(1024)
queue.put(data)
worker = threading.Thread(target=_consume, args=(queue,))
worker.setDaemon(True)
worker.start()
try:
while True:
try:
port = queue.get(timeout=1)
queue.task_done()
if port == 'q':
break
port = int(port)
print 'opening telnet session at localhost:%d...' % port
telnet(port)
print 'listening for rdb notifications on :6899...'
except Empty:
pass
except KeyboardInterrupt:
print 'got Ctrl-C'
queue.put('q')
def telnet(port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(2)
try:
s.connect(('0.0.0.0', port))
except Exception:
print 'unable to connect'
return
print 'connected to 0.0.0.0:%d' % port
while True:
socket_list = [sys.stdin, s]
r, w, e = select.select(socket_list , [], [])
for sock in r:
if sock == s:
data = sock.recv(4096)
if not data:
print 'connection closed'
return
else:
sys.stdout.write(data)
else:
msg = sys.stdin.readline()
s.send(msg)
if __name__ == '__main__':
listen()