rabin 7 years ago
parent
commit
5d7ec644d1
2 changed files with 112 additions and 11 deletions
  1. 19 11
      jupyter-mysql-kernel/kernel.py
  2. 93 0
      jupyter-mysql-kernel/parser.py

+ 19 - 11
jupyter-mysql-kernel/kernel.py

@@ -7,6 +7,7 @@ from pexpect import replwrap, EOF
 import pexpect
 
 from subprocess import check_output
+from .parser import MysqlParser
 import os.path
 
 import re
@@ -21,14 +22,12 @@ class MysqlWrapper(replwrap.REPLWrapper):
 	def __init__(self, cmd_or_spawn, orig_prompt, prompt_change,
 				 extra_init_cmd=None, line_output_callback=None):
 		self.line_output_callback = line_output_callback
-		replwrap.REPLWrapper.__init__(self, cmd_or_spawn, orig_prompt,
-									  prompt_change, extra_init_cmd=extra_init_cmd)
+		replwrap.REPLWrapper.__init__(self, cmd_or_spawn, orig_prompt, prompt_change, extra_init_cmd=extra_init_cmd)
 
 	def _expect_prompt(self, timeout=-1):
 		if timeout == None:
 			while True:
-				pos = self.child.expect_exact([self.prompt, self.continuation_prompt, u'\r\n'],
-											  timeout=None)
+				pos = self.child.expect_exact([self.prompt, self.continuation_prompt, u'\r\n'], timeout=None)
 				if pos == 2:
 					self.line_output_callback(self.child.before + '\n')
 				else:
@@ -67,7 +66,7 @@ class MysqlKernel(Kernel):
 	   ,'user'	  : 'root'
 	   ,'host'	  : '192.168.15.10'
 	   ,'port'	  : '3309'
-	   ,'charset'   : 'utf8'
+	   ,'charset'   : 'utf-8'
 	   ,'password'  : '123456'
 	}
 
@@ -83,20 +82,29 @@ class MysqlKernel(Kernel):
 		sig = signal.signal(signal.SIGINT, signal.SIG_DFL)
 		try:
 			if 'password' in self.mysql_config:
-				child = pexpect.spawn('mysql -A -h {host} -P {port} -u {user} -p{password}'.format(**self.mysql_config), echo=False, encoding='utf-8', codec_errors='replace')
+				child = pexpect.spawn('mysql -A -h {host} -P {port} -u {user} -p{password}'.format(**self.mysql_config), echo=False, encoding=self.mysql_config['charset'], codec_errors='replace')
 			else:
 				child = pexpect.spawn('mysql -A -h {host} -P {port} -u {user}'.format(**self.mysql_config))
 			prompt_change = None
 			init_cmd = None
-			self.wrapper = MysqlWrapper(child, u'>', prompt_change=prompt_change, extra_init_cmd=init_cmd, line_output_callback=self.process_output)
+			self.wrapper = MysqlWrapper(child, self.prompt, prompt_change=prompt_change, extra_init_cmd=init_cmd, line_output_callback=self.process_output)
 		finally:
 			signal.signal(signal.SIGINT, sig)
 
 	def process_output(self, output):
-		if not self.silent:
-			#output = output.decode(self.mysql_config['charset'])
-			stream_content = {'name': 'stdout', 'text': output}
-			self.send_response(self.iopub_socket, 'stream', stream_content)
+		if not self.silent and '|' in output:
+			output = output.decode(self.mysql_config['charset'])
+			output = MysqlParser(output)
+			#stream_content = {'name': 'stdout', 'text': output}
+			#self.send_response(self.iopub_socket, 'stream', stream_content)
+			display_content = {
+				'source': 'kernel',
+				'data': {
+					'text/plain': output.text(),
+					'text/html': output.html()
+				}, 'metadata': {}
+			}
+			self.send_response(self.iopub_socket, 'display_data', display_content)
 
 	def do_execute(self, code, silent, store_history=True,
 				   user_expressions=None, allow_stdin=False):

+ 93 - 0
jupyter-mysql-kernel/parser.py

@@ -0,0 +1,93 @@
+"""
+	jupyter-mysql-kernel
+	author:rabin
+"""
+
+class MysqlParser(object):
+
+    def __init__(self, response, commands=False):
+        self.response = response
+        self.result = []
+        self.is_array = False
+        self.is_error = False
+        if not commands:
+            self.parse_response()
+        else:
+            self.parse_commands()
+
+    def parse_commands(self):
+        # get each section of the command response
+        sections = self.response.split('*6\r\n')
+        for section in sections:
+            parts = section.split('\r\n')
+            # only the second one is the command name
+            # no need to parse for now
+            if parts[1] is not None and parts[1].__len__() > 0:
+                self.result.append(parts[1])
+
+    def parse_response(self):
+        # get each line of the response
+        parts = self.response.split('\r\n')
+
+        if parts[0].startswith('*'):
+            self.is_array = True
+
+        for part in parts:
+            if part != '':
+                value = self.parse_part(part)
+                if value is not None:
+                    self.result.append(value)
+
+    def parse_part(self, part):
+        if part[0] == '*':
+            # array count
+            return None
+        elif part[0] in ['-', '+', ':']:
+            if part[0] == '-':
+                self.is_error = True
+                #error or string or integer
+                return part[1:]
+            elif part[0] == '+':
+                return part[1:]
+            elif part[0] == ':':
+                return int(part[1:])
+        elif part[0] == '$':
+            if part[1:] == '-1':
+                # handle nil
+                return 'nil'
+            else:
+                # ignore the byte count
+                return None
+        else:
+            # values returned after the type specifier
+            return part
+
+    def html(self):
+        out = None
+        res = self.get_result()
+        if self.is_error:
+            out = "<p style='color:red'>" + res + '</p>'
+        else:
+            out = res
+        return out
+
+    def text(self):
+        return self.get_result()
+
+    def get_result(self):
+        if self.result.__len__() > 1:
+            out = []
+            for x in self.result:
+                if isinstance(x, int):
+                    out.append(str(x))
+                else:
+                    out.append(x)
+            return out
+        elif self.result.__len__() > 0:
+            if type(self.result[0] == int):
+                return str(self.result[0])
+            else:
+                return self.result[0]
+        else:
+            self.is_error = True
+            return 'Error executing command. There was no result.'