comparison c_kernel/kernel.py @ 2:b46b2e5b6c08

Improve error management by separating stdout and stderr
author Brendan Rius <brendan@omixy.com>
date Fri, 25 Mar 2016 14:08:26 +0000
parents 50ea00cf5896
children 8ddfdd2a8574
comparison
equal deleted inserted replaced
1:5618ac3a53f5 2:b46b2e5b6c08
1 from ipykernel.kernelbase import Kernel 1 from ipykernel.kernelbase import Kernel
2 import subprocess 2 import subprocess
3 import tempfile 3 import tempfile
4 import os
4 5
5 6
6 class CKernel(Kernel): 7 class CKernel(Kernel):
7 implementation = 'c_kernel' 8 implementation = 'c_kernel'
8 implementation_version = '1.0' 9 implementation_version = '1.0'
9 language = 'c' 10 language = 'c'
10 language_version = 'C11' 11 language_version = 'C11'
11 language_info = {'name': 'c', 'mimetype': 'text/plain', 'file_extension': 'c'} 12 language_info = {'name': 'c',
12 _banner = None 13 'mimetype': 'text/plain',
14 'file_extension': 'c'}
15 banner = "C kernel.\n" \
16 "Uses gcc, compiles in C11, and creates source code files and executables in temporary folder.\n"
13 17
14 @property 18 def __init__(self, *args, **kwargs):
15 def banner(self): 19 super(CKernel, self).__init__(*args, **kwargs)
16 if self._banner is None: 20 self.files = []
17 self._banner = subprocess.check_output(['gcc', '-v']).decode('utf-8') 21
18 return self._banner 22 def cleanup_files(self):
23 """Remove all the temporary files created by the kernel"""
24 for file in self.files:
25 os.remove(file)
26
27 def new_temp_file(self, **kwargs):
28 """Create a new temp file to be deleted when the kernel shuts down"""
29 # We don't want the file to be deleted when closed, but only when the kernel stops
30 kwargs['delete'] = False
31 file = tempfile.NamedTemporaryFile(**kwargs)
32 self.files.append(file.name)
33 return file
34
35 @staticmethod
36 def execute_command(cmd):
37 """Execute a command and returns the return code, stdout and stderr"""
38 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
39 stdout, stderr = p.communicate()
40 return p.returncode, stdout, stderr
41
42 @staticmethod
43 def compile_with_gcc(source_filename, binary_filename):
44 args = ['gcc', source_filename, '-std=c11', '-o', binary_filename]
45 return CKernel.execute_command(args)
19 46
20 def do_execute(self, code, silent, store_history=True, 47 def do_execute(self, code, silent, store_history=True,
21 user_expressions=None, allow_stdin=False): 48 user_expressions=None, allow_stdin=False):
22 code = code.strip()
23 if not code:
24 return {'status': 'ok',
25 'execution_count': self.execution_count,
26 'payload': [],
27 'user_expressions': {}}
28 49
29 output = '### COMPILATION ###\n' 50 retcode, stdout, stderr = None, '', ''
30 try: 51 with self.new_temp_file(suffix='.c') as source_file:
31 sourcefile = tempfile.NamedTemporaryFile(suffix='.c', delete=False) 52 source_file.write(code)
32 sourcefile.write(code) 53 source_file.flush()
33 sourcefile.close() 54 with self.new_temp_file(suffix='.out') as binary_file:
34 binaryfile = tempfile.NamedTemporaryFile(suffix='.out', delete=False) 55 retcode, stdout, stderr = self.compile_with_gcc(source_file.name, binary_file.name)
35 binaryfile.close() 56 self.log.error(retcode)
36 output += subprocess.check_output(['gcc', '-std=c11', sourcefile.name, '-o', binaryfile.name], 57 self.log.error(stdout)
37 stderr=subprocess.STDOUT).decode('utf-8') 58 self.log.error(stderr)
38 except subprocess.CalledProcessError as e:
39 print(e)
40 return {'status': 'error', 'ename': 'Compilation error', 'evalue': e.output}
41 59
42 output += '\n### EXECUTION ###\n' 60 retcode, out, err = CKernel.execute_command([binary_file.name])
43 try: 61 stdout += out
44 output += subprocess.check_output([binaryfile.name], stderr=subprocess.STDOUT).decode('utf-8') 62 stderr += err
45 except subprocess.CalledProcessError as e: 63 self.log.error(retcode)
46 output += e.output 64 self.log.error(out)
65 self.log.error(err)
66
47 if not silent: 67 if not silent:
48 stream_content = {'name': 'stdout', 'text': output} 68 stream_content = {'name': 'stderr', 'text': stderr}
49 self.send_response(self.iopub_socket, 'stream', stream_content) 69 self.send_response(self.iopub_socket, 'stream', stream_content)
70 stream_content = {'name': 'stdout', 'text': stdout}
71 self.send_response(self.iopub_socket, 'stream', stream_content)
72 return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}}