##// END OF EJS Templates
Merging recent changes.
Merging recent changes.

File last commit:

r1234:52b55407
r1343:7257b2cb merge
Show More
contexts.py
178 lines | 5.5 KiB | text/x-python | PythonLexer
# encoding: utf-8
# -*- test-case-name: IPython.kernel.test.test_contexts -*-
"""Context managers for IPython.
Python 2.5 introduced the `with` statement, which is based on the context
manager protocol. This module offers a few context managers for common cases,
which can also be useful as templates for writing new, application-specific
managers.
"""
from __future__ import with_statement
__docformat__ = "restructuredtext en"
#-------------------------------------------------------------------------------
# Copyright (C) 2008 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Imports
#-------------------------------------------------------------------------------
import linecache
import sys
from twisted.internet.error import ConnectionRefusedError
from IPython.ultraTB import _fixed_getinnerframes, findsource
from IPython import ipapi
from IPython.kernel import error
#---------------------------------------------------------------------------
# Utility functions needed by all context managers.
#---------------------------------------------------------------------------
def remote():
"""Raises a special exception meant to be caught by context managers.
"""
m = 'Special exception to stop local execution of parallel code.'
raise error.StopLocalExecution(m)
def strip_whitespace(source,require_remote=True):
"""strip leading whitespace from input source.
:Parameters:
"""
remote_mark = 'remote()'
# Expand tabs to avoid any confusion.
wsource = [l.expandtabs(4) for l in source]
# Detect the indentation level
done = False
for line in wsource:
if line.isspace():
continue
for col,char in enumerate(line):
if char != ' ':
done = True
break
if done:
break
# Now we know how much leading space there is in the code. Next, we
# extract up to the first line that has less indentation.
# WARNINGS: we skip comments that may be misindented, but we do NOT yet
# detect triple quoted strings that may have flush left text.
for lno,line in enumerate(wsource):
lead = line[:col]
if lead.isspace():
continue
else:
if not lead.lstrip().startswith('#'):
break
# The real 'with' source is up to lno
src_lines = [l[col:] for l in wsource[:lno+1]]
# Finally, check that the source's first non-comment line begins with the
# special call 'remote()'
if require_remote:
for nline,line in enumerate(src_lines):
if line.isspace() or line.startswith('#'):
continue
if line.startswith(remote_mark):
break
else:
raise ValueError('%s call missing at the start of code' %
remote_mark)
out_lines = src_lines[nline+1:]
else:
# If the user specified that the remote() call wasn't mandatory
out_lines = src_lines
# src = ''.join(out_lines) # dbg
#print 'SRC:\n<<<<<<<>>>>>>>\n%s<<<<<>>>>>>' % src # dbg
return ''.join(out_lines)
class RemoteContextBase(object):
def __init__(self):
self.ip = ipapi.get()
def _findsource_file(self,f):
linecache.checkcache()
s = findsource(f.f_code)
lnum = f.f_lineno
wsource = s[0][f.f_lineno:]
return strip_whitespace(wsource)
def _findsource_ipython(self,f):
from IPython import ipapi
self.ip = ipapi.get()
buf = self.ip.IP.input_hist_raw[-1].splitlines()[1:]
wsource = [l+'\n' for l in buf ]
return strip_whitespace(wsource)
def findsource(self,frame):
local_ns = frame.f_locals
global_ns = frame.f_globals
if frame.f_code.co_filename == '<ipython console>':
src = self._findsource_ipython(frame)
else:
src = self._findsource_file(frame)
return src
def __enter__(self):
raise NotImplementedError
def __exit__ (self, etype, value, tb):
if issubclass(etype,error.StopLocalExecution):
return True
class RemoteMultiEngine(RemoteContextBase):
def __init__(self,mec):
self.mec = mec
RemoteContextBase.__init__(self)
def __enter__(self):
src = self.findsource(sys._getframe(1))
return self.mec.execute(src)
# XXX - Temporary hackish testing, we'll move this into proper tests right
# away
if __name__ == '__main__':
# XXX - for now, we need a running cluster to be started separately. The
# daemon work is almost finished, and will make much of this unnecessary.
from IPython.kernel import client
mec = client.MultiEngineClient(('127.0.0.1',10105))
try:
mec.get_ids()
except ConnectionRefusedError:
import os, time
os.system('ipcluster -n 2 &')
time.sleep(2)
mec = client.MultiEngineClient(('127.0.0.1',10105))
mec.block = False
import itertools
c = itertools.count()
parallel = RemoteMultiEngine(mec)
with parallel as pr:
# A comment
remote() # this means the code below only runs remotely
print 'Hello remote world'
x = 3.14
# Comments are OK
# Even misindented.
y = x+1