[BACK]Return to kernel.py CVS log [TXT][DIR] Up to [local] / OpenXM / src / jupyter

Annotation of OpenXM/src/jupyter/kernel.py, Revision 1.1

1.1     ! takayama    1: # coding: utf-8
        !             2: # $OpenXM$
        !             3: from __future__ import print_function
        !             4:
        !             5: import codecs
        !             6: import glob
        !             7: import json
        !             8: import logging
        !             9: import os
        !            10: import re
        !            11: import shutil
        !            12: import subprocess
        !            13: import sys
        !            14: import tempfile
        !            15: import uuid
        !            16: from xml.dom import minidom
        !            17:
        !            18: from metakernel import MetaKernel, ProcessMetaKernel, REPLWrapper, u
        !            19: from metakernel.pexpect import which
        !            20: from IPython.display import Image, SVG
        !            21:
        !            22: from . import __version__
        !            23:
        !            24:
        !            25: STDIN_PROMPT = '__stdin_prompt>'
        !            26: STDIN_PROMPT_REGEX = re.compile(r'\A.+?%s|debug> ' % STDIN_PROMPT)
        !            27: HELP_LINKS = [
        !            28:     {
        !            29:         'text': "Risa/Asir",
        !            30:         'url': "http://www.openxm.org",
        !            31:     },
        !            32:     {
        !            33:         'text': "Asir Kernel",
        !            34:         'url': "http://www.openxm.org",
        !            35:     },
        !            36:
        !            37: ] + MetaKernel.help_links
        !            38:
        !            39:
        !            40: def get_kernel_json():
        !            41:     """Get the kernel json for the kernel.
        !            42:     """
        !            43:     here = os.path.dirname(__file__)
        !            44:     default_json_file = os.path.join(here, 'kernel.json')
        !            45:     json_file = os.environ.get('ASIR_KERNEL_JSON', default_json_file)
        !            46:     with open(json_file) as fid:
        !            47:         data = json.load(fid)
        !            48:     data['argv'][0] = sys.executable
        !            49:     return data
        !            50:
        !            51:
        !            52: class OctaveKernel(ProcessMetaKernel):
        !            53:     implementation = 'Asir Kernel'
        !            54:     implementation_version = __version__,
        !            55:     language = 'asir'
        !            56:     help_links = HELP_LINKS
        !            57:     kernel_json = get_kernel_json()
        !            58:
        !            59:     _octave_engine = None
        !            60:     _language_version = None
        !            61:
        !            62:     @property
        !            63:     def language_version(self):
        !            64:         if self._language_version:
        !            65:             return self._language_version
        !            66:         ver = self.octave_engine.eval('version', silent=True)
        !            67:         ver = self._language_version = ver.split()[-1]
        !            68:         return ver
        !            69:
        !            70:     @property
        !            71:     def language_info(self):
        !            72:         return {'mimetype': 'text/x-octave',
        !            73:                 'name': 'c',
        !            74:                 'file_extension': '.rr',
        !            75:                 'version': self.language_version,
        !            76:                 'help_links': HELP_LINKS}
        !            77:
        !            78:     @property
        !            79:     def banner(self):
        !            80:         msg = 'Asir Kernel v%s running Risa/Asir v%s'
        !            81:         return msg % (__version__, self.language_version)
        !            82:
        !            83:     @property
        !            84:     def octave_engine(self):
        !            85:         if self._octave_engine:
        !            86:             return self._octave_engine
        !            87:         self._octave_engine = OctaveEngine(plot_settings=self.plot_settings,
        !            88:                                            error_handler=self.Error,
        !            89:                                            stdin_handler=self.raw_input,
        !            90:                                            stream_handler=self.Print,
        !            91:                                            logger=self.log)
        !            92:         return self._octave_engine
        !            93:
        !            94:     def makeWrapper(self):
        !            95:         """Start an Octave process and return a :class:`REPLWrapper` object.
        !            96:         """
        !            97:         return self.octave_engine.repl
        !            98:
        !            99:     def do_execute_direct(self, code, silent=False):
        !           100:         if code.strip() in ['quit', 'quit()', 'exit', 'exit()']:
        !           101:             self._octave_engine = None
        !           102:             self.do_shutdown(True)
        !           103:             return
        !           104: #        f = open('tmptmp.txt','a');f.write(str(code));f.close()  #####
        !           105: #        self._octave_engine.logger.debug(str(code))  #####
        !           106:         val = ProcessMetaKernel.do_execute_direct(self, code, silent=silent)
        !           107:         if not silent:
        !           108:             try:
        !           109:                 plot_dir = self.octave_engine.make_figures()
        !           110:             except Exception as e:
        !           111:                 self.Error(e)
        !           112:                 return val
        !           113:             if plot_dir:
        !           114:                 for image in self.octave_engine.extract_figures(plot_dir, True):
        !           115:                     self.Display(image)
        !           116:         return val
        !           117:
        !           118:     def get_kernel_help_on(self, info, level=0, none_on_fail=False):
        !           119:         obj = info.get('help_obj', '')
        !           120:         if not obj or len(obj.split()) > 1:
        !           121:             if none_on_fail:
        !           122:                 return None
        !           123:             else:
        !           124:                 return ""
        !           125:         return self.octave_engine.eval('help %s' % obj, silent=True)
        !           126:
        !           127:     def Print(self, *args, **kwargs):
        !           128:         # Ignore standalone input hook displays.
        !           129:         out = []
        !           130:         for arg in args:
        !           131:             if arg.strip() == STDIN_PROMPT:
        !           132:                 return
        !           133:             if arg.strip().startswith(STDIN_PROMPT):
        !           134:                 arg = arg.replace(STDIN_PROMPT, '')
        !           135:             out.append(arg)
        !           136:         super(OctaveKernel, self).Print(*out, **kwargs)
        !           137:
        !           138:     def raw_input(self, text):
        !           139:         # Remove the stdin prompt to restore the original prompt.
        !           140:         text = text.replace(STDIN_PROMPT, '')
        !           141:         return super(OctaveKernel, self).raw_input(text)
        !           142:
        !           143:     def get_completions(self, info):
        !           144:         """
        !           145:         Get completions from kernel based on info dict.
        !           146:         """
        !           147:         cmd = 'completion_matches("%s")' % info['obj']
        !           148:         val = self.octave_engine.eval(cmd, silent=True)
        !           149:         return val and val.splitlines() or []
        !           150:
        !           151:     def handle_plot_settings(self):
        !           152:         """Handle the current plot settings"""
        !           153:         self.octave_engine.plot_settings = self.plot_settings
        !           154:
        !           155:
        !           156: class OctaveEngine(object):
        !           157:
        !           158:     def __init__(self, error_handler=None, stream_handler=None,
        !           159:                  stdin_handler=None, plot_settings=None,
        !           160:                  logger=None):
        !           161:         self.logger = logger
        !           162:         self.executable = self._get_executable()
        !           163:         self.repl = self._create_repl()
        !           164:         self.error_handler = error_handler
        !           165:         self.stream_handler = stream_handler
        !           166:         self.stdin_handler = stdin_handler
        !           167:         self._startup(plot_settings)
        !           168:
        !           169:     @property
        !           170:     def plot_settings(self):
        !           171:         return None
        !           172:         return self._plot_settings
        !           173:
        !           174:     @plot_settings.setter
        !           175:     def plot_settings(self, settings):
        !           176:         settings = settings or dict(backend='inline')
        !           177:         return None
        !           178:         self._plot_settings = settings
        !           179:
        !           180:         # Remove "None" keys so we can use setdefault below.
        !           181:         keys = ['format', 'backend', 'width', 'height', 'resolution',
        !           182:                 'backend', 'name']
        !           183:         for key in keys:
        !           184:             if key in settings and settings.get(key, None) is None:
        !           185:                 del settings[key]
        !           186:
        !           187:         if sys.platform == 'darwin':
        !           188:             settings.setdefault('format', 'svg')
        !           189:         else:
        !           190:             settings.setdefault('format', 'png')
        !           191:
        !           192:         settings.setdefault('backend', 'inline')
        !           193:         settings.setdefault('width', -1)
        !           194:         settings.setdefault('height', -1)
        !           195:         settings.setdefault('resolution', 0)
        !           196:         settings.setdefault('name', 'Figure')
        !           197:
        !           198:         cmds = []
        !           199:         if settings['backend'] == 'inline':
        !           200:             cmds.append("set(0, 'defaultfigurevisible', 'off');")
        !           201:         else:
        !           202:             cmds.append("set(0, 'defaultfigurevisible', 'on');")
        !           203:             cmds.append("graphics_toolkit('%s');" % settings['backend'])
        !           204:         self.eval('\n'.join(cmds))
        !           205:
        !           206:     def eval(self, code, timeout=None, silent=False):
        !           207:         """Evaluate code using the engine.
        !           208:         """
        !           209:         stream_handler = None if silent else self.stream_handler
        !           210:         if self.logger:
        !           211: #            self.logger.setLevel(logging.DEBUG)  ####
        !           212:             self.logger.debug('Asir eval:')
        !           213:             self.logger.debug(code)
        !           214:         try:
        !           215:             resp = self.repl.run_command(code.rstrip(),
        !           216:                                          timeout=timeout,
        !           217:                                          stream_handler=stream_handler,
        !           218:                                          stdin_handler=self.stdin_handler)
        !           219:             resp = resp.replace(STDIN_PROMPT, '')
        !           220:             if self.logger and resp:
        !           221:                 self.logger.debug(resp)
        !           222:             return resp
        !           223:         except KeyboardInterrupt:
        !           224:             return self._interrupt(True)
        !           225:         except Exception as e:
        !           226:             if self.error_handler:
        !           227:                 self.error_handler(e)
        !           228:             else:
        !           229:                 raise e
        !           230:
        !           231:     def make_figures(self, plot_dir=None):
        !           232:         """Create figures for the current figures.
        !           233:
        !           234:         Parameters
        !           235:         ----------
        !           236:         plot_dir: str, optional
        !           237:             The directory in which to create the plots.
        !           238:
        !           239:         Returns
        !           240:         -------
        !           241:         out: str
        !           242:             The plot directory containing the files.
        !           243:         """
        !           244:         return None
        !           245:         settings = self.plot_settings
        !           246:         if settings['backend'] != 'inline':
        !           247:             self.eval('drawnow("expose");')
        !           248:             if not plot_dir:
        !           249:                 return
        !           250:         fmt = settings['format']
        !           251:         res = settings['resolution']
        !           252:         wid = settings['width']
        !           253:         hgt = settings['height']
        !           254:         name = settings['name']
        !           255:         plot_dir = plot_dir or tempfile.mkdtemp()
        !           256:         plot_dir = plot_dir.replace(os.path.sep, '/')
        !           257:
        !           258:         # Do not overwrite any existing plot files.
        !           259:         spec = os.path.join(plot_dir, '%s*' % name)
        !           260:         start = len(glob.glob(spec))
        !           261:
        !           262:         make_figs = '_make_figures("%s", "%s", "%s", %d, %d, %d, %d)'
        !           263:         make_figs = make_figs % (plot_dir, fmt, name, wid, hgt, res, start)
        !           264:         resp = self.eval(make_figs, silent=True)
        !           265:         msg = 'Inline plot failed, consider trying another graphics toolkit\n'
        !           266:         if resp and 'error:' in resp:
        !           267:             resp = msg + resp
        !           268:             if self.error_handler:
        !           269:                 self.error_handler(resp)
        !           270:             else:
        !           271:                 raise Exception(resp)
        !           272:         return plot_dir
        !           273:
        !           274:     def extract_figures(self, plot_dir, remove=False):
        !           275:         """Get a list of IPython Image objects for the created figures.
        !           276:
        !           277:         Parameters
        !           278:         ----------
        !           279:         plot_dir: str
        !           280:             The directory in which to create the plots.
        !           281:         remove: bool, optional.
        !           282:             Whether to remove the plot directory after saving.
        !           283:         """
        !           284:         images = []
        !           285:         spec = os.path.join(plot_dir, '%s*' % self.plot_settings['name'])
        !           286:         for fname in reversed(glob.glob(spec)):
        !           287:             filename = os.path.join(plot_dir, fname)
        !           288:             try:
        !           289:                 if fname.lower().endswith('.svg'):
        !           290:                     im = self._handle_svg(filename)
        !           291:                 else:
        !           292:                     im = Image(filename)
        !           293:                 images.append(im)
        !           294:             except Exception as e:
        !           295:                 if self.error_handler:
        !           296:                     self.error_handler(e)
        !           297:                 else:
        !           298:                     raise e
        !           299:         if remove:
        !           300:             shutil.rmtree(plot_dir, True)
        !           301:         return images
        !           302:
        !           303:     def _startup(self, plot_settings):
        !           304:         return None
        !           305:
        !           306:     def _handle_svg(self, filename):
        !           307:         """
        !           308:         Handle special considerations for SVG images.
        !           309:         """
        !           310:         # Gnuplot can create invalid characters in SVG files.
        !           311:         with codecs.open(filename, 'r', encoding='utf-8',
        !           312:                          errors='replace') as fid:
        !           313:             data = fid.read()
        !           314:         im = SVG(data=data)
        !           315:         try:
        !           316:             im.data = self._fix_svg_size(im.data)
        !           317:         except Exception:
        !           318:             pass
        !           319:         return im
        !           320:
        !           321:     def _fix_svg_size(self, data):
        !           322:         """GnuPlot SVGs do not have height/width attributes.  Set
        !           323:         these to be the same as the viewBox, so that the browser
        !           324:         scales the image correctly.
        !           325:         """
        !           326:         # Minidom does not support parseUnicode, so it must be decoded
        !           327:         # to accept unicode characters
        !           328:         parsed = minidom.parseString(data.encode('utf-8'))
        !           329:         (svg,) = parsed.getElementsByTagName('svg')
        !           330:
        !           331:         viewbox = svg.getAttribute('viewBox').split(' ')
        !           332:         width, height = viewbox[2:]
        !           333:         width, height = int(width), int(height)
        !           334:
        !           335:         # Handle overrides in case they were not encoded.
        !           336:         settings = self.plot_settings
        !           337:         if settings['width'] != -1:
        !           338:             if settings['height'] == -1:
        !           339:                 height = height * settings['width'] / width
        !           340:             width = settings['width']
        !           341:         if settings['height'] != -1:
        !           342:             if settings['width'] == -1:
        !           343:                 width = width * settings['height'] / height
        !           344:             height = settings['height']
        !           345:
        !           346:         svg.setAttribute('width', '%dpx' % width)
        !           347:         svg.setAttribute('height', '%dpx' % height)
        !           348:         return svg.toxml()
        !           349:
        !           350:     def _create_repl(self):
        !           351:         cmd = self.executable
        !           352:         if 'asir-cli' not in cmd:
        !           353:             version_cmd = [self.executable, '--version']
        !           354:             version = subprocess.check_output(version_cmd).decode('utf-8')
        !           355:             if 'version 4' in version:
        !           356:                 cmd += ' --no-gui'
        !           357:         # Interactive mode prevents crashing on Windows on syntax errors.
        !           358:         # Delay sourcing the "~/.octaverc" file in case it displays a pager.
        !           359:         cmd += ' --interactive --quiet --no-init-file '
        !           360:
        !           361:         # Add cli options provided by the user.
        !           362:         cmd += os.environ.get('OCTAVE_CLI_OPTIONS', '')
        !           363:
        !           364:         orig_prompt = u('PEXPECT_PROMPT>')
        !           365:         change_prompt = u("base_prompt('{0}')")
        !           366:
        !           367:         repl = REPLWrapper(cmd, orig_prompt, change_prompt,
        !           368:                            stdin_prompt_regex=STDIN_PROMPT_REGEX)
        !           369:         if os.name == 'nt':
        !           370:             repl.child.crlf = '\n'
        !           371:         repl.interrupt = self._interrupt
        !           372:         # Remove the default 50ms delay before sending lines.
        !           373:         repl.child.delaybeforesend = None
        !           374:         return repl
        !           375:
        !           376:     def _interrupt(self, silent=False):
        !           377:         if (os.name == 'nt'):
        !           378:             msg = '** Warning: Cannot interrupt Octave on Windows'
        !           379:             if self.stream_handler:
        !           380:                 self.stream_handler(msg)
        !           381:             elif self.logger:
        !           382:                 self.logger.warn(msg)
        !           383:             return self._interrupt_expect(silent)
        !           384:         return REPLWrapper.interrupt(self.repl)
        !           385:
        !           386:     def _interrupt_expect(self, silent):
        !           387:         repl = self.repl
        !           388:         child = repl.child
        !           389:         expects = [repl.prompt_regex, child.linesep]
        !           390:         expected = uuid.uuid4().hex
        !           391:         repl.sendline('disp("%s");' % expected)
        !           392:         if repl.prompt_emit_cmd:
        !           393:             repl.sendline(repl.prompt_emit_cmd)
        !           394:         lines = []
        !           395:         while True:
        !           396:             # Prevent a keyboard interrupt from breaking this up.
        !           397:             while True:
        !           398:                 try:
        !           399:                     pos = child.expect(expects)
        !           400:                     break
        !           401:                 except KeyboardInterrupt:
        !           402:                     pass
        !           403:             if pos == 1:  # End of line received
        !           404:                 line = child.before
        !           405:                 if silent:
        !           406:                     lines.append(line)
        !           407:                 else:
        !           408:                     self.stream_handler(line)
        !           409:             else:
        !           410:                 line = child.before
        !           411:                 if line.strip() == expected:
        !           412:                     break
        !           413:                 if len(line) != 0:
        !           414:                     # prompt received, but partial line precedes it
        !           415:                     if silent:
        !           416:                         lines.append(line)
        !           417:                     else:
        !           418:                         self.stream_handler(line)
        !           419:         return '\n'.join(lines)
        !           420:
        !           421:     def _get_executable(self):
        !           422:         """Find the best octave executable.
        !           423:         """
        !           424:         executable = os.environ.get('ASIR_EXECUTABLE', None)
        !           425:         if not executable or not which(executable):
        !           426:             if which('asir-cli'):
        !           427:                 executable = 'asir-cli'
        !           428:             else:
        !           429:                 msg = ('asir-cli Executable not found, please add to path or set'
        !           430:                        '"ASIR_EXECUTABLE" environment variable')
        !           431:                 raise OSError(msg)
        !           432:         executable = executable.replace(os.path.sep, '/')
        !           433:         return executable

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>