##// END OF EJS Templates
Merging in Brian's branch (also pulls Gael's work)....
Fernando Perez -
r1720:a2890974 merge
parent child Browse files
Show More
@@ -0,0 +1,155 b''
1 # encoding: utf-8
2
3 """This file contains unittests for the frontendbase module."""
4
5 __docformat__ = "restructuredtext en"
6
7 #---------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
13
14 #---------------------------------------------------------------------------
15 # Imports
16 #---------------------------------------------------------------------------
17
18 import unittest
19
20 try:
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27
28 from IPython.testing.decorators import skip
29
30 class FrontEndCallbackChecker(AsyncFrontEndBase):
31 """FrontEndBase subclass for checking callbacks"""
32 def __init__(self, engine=None, history=None):
33 super(FrontEndCallbackChecker, self).__init__(engine=engine,
34 history=history)
35 self.updateCalled = False
36 self.renderResultCalled = False
37 self.renderErrorCalled = False
38
39 def update_cell_prompt(self, result, blockID=None):
40 self.updateCalled = True
41 return result
42
43 def render_result(self, result):
44 self.renderResultCalled = True
45 return result
46
47
48 def render_error(self, failure):
49 self.renderErrorCalled = True
50 return failure
51
52
53
54
55 class TestAsyncFrontendBase(unittest.TestCase):
56 def setUp(self):
57 """Setup the EngineService and FrontEndBase"""
58
59 self.fb = FrontEndCallbackChecker(engine=EngineService())
60
61 def test_implements_IFrontEnd(self):
62 assert(frontendbase.IFrontEnd.implementedBy(
63 AsyncFrontEndBase))
64
65 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
67
68 block = """def test(a):"""
69
70 assert(self.fb.is_complete(block) == False)
71
72 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
74
75 block = """def test(a): pass"""
76
77 assert(self.fb.is_complete(block))
78
79 block = """a=3"""
80
81 assert(self.fb.is_complete(block))
82
83 def test_blockID_added_to_result(self):
84 block = """3+3"""
85
86 d = self.fb.execute(block, blockID='TEST_ID')
87
88 d.addCallback(self.checkBlockID, expected='TEST_ID')
89
90 def test_blockID_added_to_failure(self):
91 block = "raise Exception()"
92
93 d = self.fb.execute(block,blockID='TEST_ID')
94 d.addErrback(self.checkFailureID, expected='TEST_ID')
95
96 def checkBlockID(self, result, expected=""):
97 assert(result['blockID'] == expected)
98
99
100 def checkFailureID(self, failure, expected=""):
101 assert(failure.blockID == expected)
102
103
104 def test_callbacks_added_to_execute(self):
105 """test that
106 update_cell_prompt
107 render_result
108
109 are added to execute request
110 """
111
112 d = self.fb.execute("10+10")
113 d.addCallback(self.checkCallbacks)
114
115 def checkCallbacks(self, result):
116 assert(self.fb.updateCalled)
117 assert(self.fb.renderResultCalled)
118
119 @skip("This test fails and lead to an unhandled error in a Deferred.")
120 def test_error_callback_added_to_execute(self):
121 """test that render_error called on execution error"""
122
123 d = self.fb.execute("raise Exception()")
124 d.addCallback(self.checkRenderError)
125
126 def checkRenderError(self, result):
127 assert(self.fb.renderErrorCalled)
128
129 def test_history_returns_expected_block(self):
130 """Make sure history browsing doesn't fail"""
131
132 blocks = ["a=1","a=2","a=3"]
133 for b in blocks:
134 d = self.fb.execute(b)
135
136 # d is now the deferred for the last executed block
137 d.addCallback(self.historyTests, blocks)
138
139
140 def historyTests(self, result, blocks):
141 """historyTests"""
142
143 assert(len(blocks) >= 3)
144 assert(self.fb.get_history_previous("") == blocks[-2])
145 assert(self.fb.get_history_previous("") == blocks[-3])
146 assert(self.fb.get_history_next() == blocks[-2])
147
148
149 def test_history_returns_none_at_startup(self):
150 """test_history_returns_none_at_startup"""
151
152 assert(self.fb.get_history_previous("")==None)
153 assert(self.fb.get_history_next()==None)
154
155
@@ -0,0 +1,20 b''
1 #!/usr/bin/env python
2 """Call the compile script to check that all code we ship compiles correctly.
3 """
4
5 import os
6 import sys
7
8
9 vstr = '.'.join(map(str,sys.version_info[:2]))
10
11 stat = os.system('python %s/lib/python%s/compileall.py .' % (sys.prefix,vstr))
12
13 print
14 if stat:
15 print '*** THERE WAS AN ERROR! ***'
16 print 'See messages above for the actual file that produced it.'
17 else:
18 print 'OK'
19
20 sys.exit(stat)
@@ -1,97 +1,121 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Release data for the IPython project."""
2 """Release data for the IPython project."""
3
3
4 #*****************************************************************************
4 #*****************************************************************************
5 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
5 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
6 #
6 #
7 # Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray
7 # Copyright (c) 2001 Janko Hauser <jhauser@zscout.de> and Nathaniel Gray
8 # <n8gray@caltech.edu>
8 # <n8gray@caltech.edu>
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #*****************************************************************************
12 #*****************************************************************************
13
13
14 # Name of the package for release purposes. This is the name which labels
14 # Name of the package for release purposes. This is the name which labels
15 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
15 # the tarballs and RPMs made by distutils, so it's best to lowercase it.
16 name = 'ipython'
16 name = 'ipython'
17
17
18 # For versions with substrings (like 0.6.16.svn), use an extra . to separate
18 # For versions with substrings (like 0.6.16.svn), use an extra . to separate
19 # the new substring. We have to avoid using either dashes or underscores,
19 # the new substring. We have to avoid using either dashes or underscores,
20 # because bdist_rpm does not accept dashes (an RPM) convention, and
20 # because bdist_rpm does not accept dashes (an RPM) convention, and
21 # bdist_deb does not accept underscores (a Debian convention).
21 # bdist_deb does not accept underscores (a Debian convention).
22
22
23 development = False # change this to False to do a release
23 development = False # change this to False to do a release
24 version_base = '0.9.1'
24 version_base = '0.9.1'
25 branch = 'ipython'
25 branch = 'ipython'
26 revision = '1143'
26 revision = '1143'
27
27
28 if development:
28 if development:
29 if branch == 'ipython':
29 if branch == 'ipython':
30 version = '%s.bzr.r%s' % (version_base, revision)
30 version = '%s.bzr.r%s' % (version_base, revision)
31 else:
31 else:
32 version = '%s.bzr.r%s.%s' % (version_base, revision, branch)
32 version = '%s.bzr.r%s.%s' % (version_base, revision, branch)
33 else:
33 else:
34 version = version_base
34 version = version_base
35
35
36
36
37 description = "Tools for interactive development in Python."
37 description = "An interactive computing environment for Python"
38
38
39 long_description = \
39 long_description = \
40 """
40 """
41 IPython provides a replacement for the interactive Python interpreter with
41 The goal of IPython is to create a comprehensive environment for
42 extra functionality.
42 interactive and exploratory computing. To support this goal, IPython
43 has two main components:
43
44
44 Main features:
45 * An enhanced interactive Python shell.
46
47 * An architecture for interactive parallel computing.
48
49 The enhanced interactive Python shell has the following main features:
45
50
46 * Comprehensive object introspection.
51 * Comprehensive object introspection.
47
52
48 * Input history, persistent across sessions.
53 * Input history, persistent across sessions.
49
54
50 * Caching of output results during a session with automatically generated
55 * Caching of output results during a session with automatically generated
51 references.
56 references.
52
57
53 * Readline based name completion.
58 * Readline based name completion.
54
59
55 * Extensible system of 'magic' commands for controlling the environment and
60 * Extensible system of 'magic' commands for controlling the environment and
56 performing many tasks related either to IPython or the operating system.
61 performing many tasks related either to IPython or the operating system.
57
62
58 * Configuration system with easy switching between different setups (simpler
63 * Configuration system with easy switching between different setups (simpler
59 than changing $PYTHONSTARTUP environment variables every time).
64 than changing $PYTHONSTARTUP environment variables every time).
60
65
61 * Session logging and reloading.
66 * Session logging and reloading.
62
67
63 * Extensible syntax processing for special purpose situations.
68 * Extensible syntax processing for special purpose situations.
64
69
65 * Access to the system shell with user-extensible alias system.
70 * Access to the system shell with user-extensible alias system.
66
71
67 * Easily embeddable in other Python programs.
72 * Easily embeddable in other Python programs and wxPython GUIs.
68
73
69 * Integrated access to the pdb debugger and the Python profiler.
74 * Integrated access to the pdb debugger and the Python profiler.
70
75
71 The latest development version is always available at the IPython subversion
76 The parallel computing architecture has the following main features:
72 repository_.
77
78 * Quickly parallelize Python code from an interactive Python/IPython session.
79
80 * A flexible and dynamic process model that be deployed on anything from
81 multicore workstations to supercomputers.
82
83 * An architecture that supports many different styles of parallelism, from
84 message passing to task farming.
85
86 * Both blocking and fully asynchronous interfaces.
87
88 * High level APIs that enable many things to be parallelized in a few lines
89 of code.
90
91 * Share live parallel jobs with other users securely.
92
93 * Dynamically load balanced task farming system.
94
95 * Robust error handling in parallel code.
73
96
74 .. _repository: http://ipython.scipy.org/svn/ipython/ipython/trunk#egg=ipython-dev
97 The latest development version is always available from IPython's `Launchpad
98 site <http://launchpad.net/ipython>`_.
75 """
99 """
76
100
77 license = 'BSD'
101 license = 'BSD'
78
102
79 authors = {'Fernando' : ('Fernando Perez','fperez@colorado.edu'),
103 authors = {'Fernando' : ('Fernando Perez','fperez@colorado.edu'),
80 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
104 'Janko' : ('Janko Hauser','jhauser@zscout.de'),
81 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
105 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'),
82 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
106 'Ville' : ('Ville Vainio','vivainio@gmail.com'),
83 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
107 'Brian' : ('Brian E Granger', 'ellisonbg@gmail.com'),
84 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com')
108 'Min' : ('Min Ragan-Kelley', 'benjaminrk@gmail.com')
85 }
109 }
86
110
87 author = 'The IPython Development Team'
111 author = 'The IPython Development Team'
88
112
89 author_email = 'ipython-dev@scipy.org'
113 author_email = 'ipython-dev@scipy.org'
90
114
91 url = 'http://ipython.scipy.org'
115 url = 'http://ipython.scipy.org'
92
116
93 download_url = 'http://ipython.scipy.org/dist'
117 download_url = 'http://ipython.scipy.org/dist'
94
118
95 platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME']
119 platforms = ['Linux','Mac OSX','Windows XP/2000/NT','Windows 95/98/ME']
96
120
97 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed']
121 keywords = ['Interactive','Interpreter','Shell','Parallel','Distributed']
@@ -1,106 +1,102 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This is the official entry point to IPython's configuration system. """
3 """This is the official entry point to IPython's configuration system. """
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 import os
18 import os
19 from os.path import join as pjoin
19 from os.path import join as pjoin
20
20
21 from IPython.genutils import get_home_dir, get_ipython_dir
21 from IPython.genutils import get_home_dir, get_ipython_dir
22 from IPython.external.configobj import ConfigObj
22 from IPython.external.configobj import ConfigObj
23
23
24 # Traitlets config imports
25 from IPython.config import traitlets
26 from IPython.config.config import *
27 from traitlets import *
28
24
29 class ConfigObjManager(object):
25 class ConfigObjManager(object):
30
26
31 def __init__(self, configObj, filename):
27 def __init__(self, configObj, filename):
32 self.current = configObj
28 self.current = configObj
33 self.current.indent_type = ' '
29 self.current.indent_type = ' '
34 self.filename = filename
30 self.filename = filename
35 # self.write_default_config_file()
31 # self.write_default_config_file()
36
32
37 def get_config_obj(self):
33 def get_config_obj(self):
38 return self.current
34 return self.current
39
35
40 def update_config_obj(self, newConfig):
36 def update_config_obj(self, newConfig):
41 self.current.merge(newConfig)
37 self.current.merge(newConfig)
42
38
43 def update_config_obj_from_file(self, filename):
39 def update_config_obj_from_file(self, filename):
44 newConfig = ConfigObj(filename, file_error=False)
40 newConfig = ConfigObj(filename, file_error=False)
45 self.current.merge(newConfig)
41 self.current.merge(newConfig)
46
42
47 def update_config_obj_from_default_file(self, ipythondir=None):
43 def update_config_obj_from_default_file(self, ipythondir=None):
48 fname = self.resolve_file_path(self.filename, ipythondir)
44 fname = self.resolve_file_path(self.filename, ipythondir)
49 self.update_config_obj_from_file(fname)
45 self.update_config_obj_from_file(fname)
50
46
51 def write_config_obj_to_file(self, filename):
47 def write_config_obj_to_file(self, filename):
52 f = open(filename, 'w')
48 f = open(filename, 'w')
53 self.current.write(f)
49 self.current.write(f)
54 f.close()
50 f.close()
55
51
56 def write_default_config_file(self):
52 def write_default_config_file(self):
57 ipdir = get_ipython_dir()
53 ipdir = get_ipython_dir()
58 fname = pjoin(ipdir, self.filename)
54 fname = pjoin(ipdir, self.filename)
59 if not os.path.isfile(fname):
55 if not os.path.isfile(fname):
60 print "Writing the configuration file to: " + fname
56 print "Writing the configuration file to: " + fname
61 self.write_config_obj_to_file(fname)
57 self.write_config_obj_to_file(fname)
62
58
63 def _import(self, key):
59 def _import(self, key):
64 package = '.'.join(key.split('.')[0:-1])
60 package = '.'.join(key.split('.')[0:-1])
65 obj = key.split('.')[-1]
61 obj = key.split('.')[-1]
66 execString = 'from %s import %s' % (package, obj)
62 execString = 'from %s import %s' % (package, obj)
67 exec execString
63 exec execString
68 exec 'temp = %s' % obj
64 exec 'temp = %s' % obj
69 return temp
65 return temp
70
66
71 def resolve_file_path(self, filename, ipythondir = None):
67 def resolve_file_path(self, filename, ipythondir = None):
72 """Resolve filenames into absolute paths.
68 """Resolve filenames into absolute paths.
73
69
74 This function looks in the following directories in order:
70 This function looks in the following directories in order:
75
71
76 1. In the current working directory or by absolute path with ~ expanded
72 1. In the current working directory or by absolute path with ~ expanded
77 2. In ipythondir if that is set
73 2. In ipythondir if that is set
78 3. In the IPYTHONDIR environment variable if it exists
74 3. In the IPYTHONDIR environment variable if it exists
79 4. In the ~/.ipython directory
75 4. In the ~/.ipython directory
80
76
81 Note: The IPYTHONDIR is also used by the trunk version of IPython so
77 Note: The IPYTHONDIR is also used by the trunk version of IPython so
82 changing it will also affect it was well.
78 changing it will also affect it was well.
83 """
79 """
84
80
85 # In cwd or by absolute path with ~ expanded
81 # In cwd or by absolute path with ~ expanded
86 trythis = os.path.expanduser(filename)
82 trythis = os.path.expanduser(filename)
87 if os.path.isfile(trythis):
83 if os.path.isfile(trythis):
88 return trythis
84 return trythis
89
85
90 # In ipythondir if it is set
86 # In ipythondir if it is set
91 if ipythondir is not None:
87 if ipythondir is not None:
92 trythis = pjoin(ipythondir, filename)
88 trythis = pjoin(ipythondir, filename)
93 if os.path.isfile(trythis):
89 if os.path.isfile(trythis):
94 return trythis
90 return trythis
95
91
96 trythis = pjoin(get_ipython_dir(), filename)
92 trythis = pjoin(get_ipython_dir(), filename)
97 if os.path.isfile(trythis):
93 if os.path.isfile(trythis):
98 return trythis
94 return trythis
99
95
100 return None
96 return None
101
97
102
98
103
99
104
100
105
101
106
102
@@ -1,76 +1,76 b''
1 """
1 """
2 Base front end class for all async frontends.
2 Base front end class for all async frontends.
3 """
3 """
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 import uuid
17 from IPython.external import guid
18
18
19
19
20 from zope.interface import Interface, Attribute, implements, classProvides
20 from zope.interface import Interface, Attribute, implements, classProvides
21 from twisted.python.failure import Failure
21 from twisted.python.failure import Failure
22 from IPython.frontend.frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
22 from IPython.frontend.frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
23 from IPython.kernel.core.history import FrontEndHistory
23 from IPython.kernel.core.history import FrontEndHistory
24 from IPython.kernel.engineservice import IEngineCore
24 from IPython.kernel.engineservice import IEngineCore
25
25
26
26
27 class AsyncFrontEndBase(FrontEndBase):
27 class AsyncFrontEndBase(FrontEndBase):
28 """
28 """
29 Overrides FrontEndBase to wrap execute in a deferred result.
29 Overrides FrontEndBase to wrap execute in a deferred result.
30 All callbacks are made as callbacks on the deferred result.
30 All callbacks are made as callbacks on the deferred result.
31 """
31 """
32
32
33 implements(IFrontEnd)
33 implements(IFrontEnd)
34 classProvides(IFrontEndFactory)
34 classProvides(IFrontEndFactory)
35
35
36 def __init__(self, engine=None, history=None):
36 def __init__(self, engine=None, history=None):
37 assert(engine==None or IEngineCore.providedBy(engine))
37 assert(engine==None or IEngineCore.providedBy(engine))
38 self.engine = IEngineCore(engine)
38 self.engine = IEngineCore(engine)
39 if history is None:
39 if history is None:
40 self.history = FrontEndHistory(input_cache=[''])
40 self.history = FrontEndHistory(input_cache=[''])
41 else:
41 else:
42 self.history = history
42 self.history = history
43
43
44
44
45 def execute(self, block, blockID=None):
45 def execute(self, block, blockID=None):
46 """Execute the block and return the deferred result.
46 """Execute the block and return the deferred result.
47
47
48 Parameters:
48 Parameters:
49 block : {str, AST}
49 block : {str, AST}
50 blockID : any
50 blockID : any
51 Caller may provide an ID to identify this block.
51 Caller may provide an ID to identify this block.
52 result['blockID'] := blockID
52 result['blockID'] := blockID
53
53
54 Result:
54 Result:
55 Deferred result of self.interpreter.execute
55 Deferred result of self.interpreter.execute
56 """
56 """
57
57
58 if(not self.is_complete(block)):
58 if(not self.is_complete(block)):
59 return Failure(Exception("Block is not compilable"))
59 return Failure(Exception("Block is not compilable"))
60
60
61 if(blockID == None):
61 if(blockID == None):
62 blockID = uuid.uuid4() #random UUID
62 blockID = guid.generate()
63
63
64 d = self.engine.execute(block)
64 d = self.engine.execute(block)
65 d.addCallback(self._add_history, block=block)
65 d.addCallback(self._add_history, block=block)
66 d.addCallbacks(self._add_block_id_for_result,
66 d.addCallbacks(self._add_block_id_for_result,
67 errback=self._add_block_id_for_failure,
67 errback=self._add_block_id_for_failure,
68 callbackArgs=(blockID,),
68 callbackArgs=(blockID,),
69 errbackArgs=(blockID,))
69 errbackArgs=(blockID,))
70 d.addBoth(self.update_cell_prompt, blockID=blockID)
70 d.addBoth(self.update_cell_prompt, blockID=blockID)
71 d.addCallbacks(self.render_result,
71 d.addCallbacks(self.render_result,
72 errback=self.render_error)
72 errback=self.render_error)
73
73
74 return d
74 return d
75
75
76
76
@@ -1,560 +1,560 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
2 # -*- test-case-name: IPython.frontend.cocoa.tests.test_cocoa_frontend -*-
3
3
4 """PyObjC classes to provide a Cocoa frontend to the
4 """PyObjC classes to provide a Cocoa frontend to the
5 IPython.kernel.engineservice.IEngineBase.
5 IPython.kernel.engineservice.IEngineBase.
6
6
7 To add an IPython interpreter to a cocoa app, instantiate an
7 To add an IPython interpreter to a cocoa app, instantiate an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
8 IPythonCocoaController in a XIB and connect its textView outlet to an
9 NSTextView instance in your UI. That's it.
9 NSTextView instance in your UI. That's it.
10
10
11 Author: Barry Wark
11 Author: Barry Wark
12 """
12 """
13
13
14 __docformat__ = "restructuredtext en"
14 __docformat__ = "restructuredtext en"
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Copyright (C) 2008 The IPython Development Team
17 # Copyright (C) 2008 The IPython Development Team
18 #
18 #
19 # Distributed under the terms of the BSD License. The full license is in
19 # Distributed under the terms of the BSD License. The full license is in
20 # the file COPYING, distributed as part of this software.
20 # the file COPYING, distributed as part of this software.
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Imports
24 # Imports
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 import sys
27 import sys
28 import objc
28 import objc
29 import uuid
29 from IPython.external import guid
30
30
31 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
31 from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
32 NSLog, NSNotificationCenter, NSMakeRange,\
33 NSLocalizedString, NSIntersectionRange,\
33 NSLocalizedString, NSIntersectionRange,\
34 NSString, NSAutoreleasePool
34 NSString, NSAutoreleasePool
35
35
36 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
36 from AppKit import NSApplicationWillTerminateNotification, NSBeep,\
37 NSTextView, NSRulerView, NSVerticalRuler
37 NSTextView, NSRulerView, NSVerticalRuler
38
38
39 from pprint import saferepr
39 from pprint import saferepr
40
40
41 import IPython
41 import IPython
42 from IPython.kernel.engineservice import ThreadedEngineService
42 from IPython.kernel.engineservice import ThreadedEngineService
43 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
43 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
44
44
45 from twisted.internet.threads import blockingCallFromThread
45 from twisted.internet.threads import blockingCallFromThread
46 from twisted.python.failure import Failure
46 from twisted.python.failure import Failure
47
47
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Classes to implement the Cocoa frontend
49 # Classes to implement the Cocoa frontend
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 # TODO:
52 # TODO:
53 # 1. use MultiEngineClient and out-of-process engine rather than
53 # 1. use MultiEngineClient and out-of-process engine rather than
54 # ThreadedEngineService?
54 # ThreadedEngineService?
55 # 2. integrate Xgrid launching of engines
55 # 2. integrate Xgrid launching of engines
56
56
57 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
57 class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService):
58 """Wrap all blocks in an NSAutoreleasePool"""
58 """Wrap all blocks in an NSAutoreleasePool"""
59
59
60 def wrapped_execute(self, msg, lines):
60 def wrapped_execute(self, msg, lines):
61 """wrapped_execute"""
61 """wrapped_execute"""
62 try:
62 try:
63 p = NSAutoreleasePool.alloc().init()
63 p = NSAutoreleasePool.alloc().init()
64 result = super(AutoreleasePoolWrappedThreadedEngineService,
64 result = super(AutoreleasePoolWrappedThreadedEngineService,
65 self).wrapped_execute(msg, lines)
65 self).wrapped_execute(msg, lines)
66 finally:
66 finally:
67 p.drain()
67 p.drain()
68
68
69 return result
69 return result
70
70
71
71
72
72
73 class Cell(NSObject):
73 class Cell(NSObject):
74 """
74 """
75 Representation of the prompts, input and output of a cell in the
75 Representation of the prompts, input and output of a cell in the
76 frontend
76 frontend
77 """
77 """
78
78
79 blockNumber = objc.ivar().unsigned_long()
79 blockNumber = objc.ivar().unsigned_long()
80 blockID = objc.ivar()
80 blockID = objc.ivar()
81 inputBlock = objc.ivar()
81 inputBlock = objc.ivar()
82 output = objc.ivar()
82 output = objc.ivar()
83
83
84
84
85
85
86 class CellBlock(object):
86 class CellBlock(object):
87 """
87 """
88 Storage for information about text ranges relating to a single cell
88 Storage for information about text ranges relating to a single cell
89 """
89 """
90
90
91
91
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
92 def __init__(self, inputPromptRange, inputRange=None, outputPromptRange=None,
93 outputRange=None):
93 outputRange=None):
94 super(CellBlock, self).__init__()
94 super(CellBlock, self).__init__()
95 self.inputPromptRange = inputPromptRange
95 self.inputPromptRange = inputPromptRange
96 self.inputRange = inputRange
96 self.inputRange = inputRange
97 self.outputPromptRange = outputPromptRange
97 self.outputPromptRange = outputPromptRange
98 self.outputRange = outputRange
98 self.outputRange = outputRange
99
99
100 def update_ranges_for_insertion(self, text, textRange):
100 def update_ranges_for_insertion(self, text, textRange):
101 """Update ranges for text insertion at textRange"""
101 """Update ranges for text insertion at textRange"""
102
102
103 for r in [self.inputPromptRange,self.inputRange,
103 for r in [self.inputPromptRange,self.inputRange,
104 self.outputPromptRange, self.outputRange]:
104 self.outputPromptRange, self.outputRange]:
105 if(r == None):
105 if(r == None):
106 continue
106 continue
107 intersection = NSIntersectionRange(r,textRange)
107 intersection = NSIntersectionRange(r,textRange)
108 if(intersection.length == 0): #ranges don't intersect
108 if(intersection.length == 0): #ranges don't intersect
109 if r.location >= textRange.location:
109 if r.location >= textRange.location:
110 r.location += len(text)
110 r.location += len(text)
111 else: #ranges intersect
111 else: #ranges intersect
112 if(r.location > textRange.location):
112 if(r.location > textRange.location):
113 offset = len(text) - intersection.length
113 offset = len(text) - intersection.length
114 r.length -= offset
114 r.length -= offset
115 r.location += offset
115 r.location += offset
116 elif(r.location == textRange.location):
116 elif(r.location == textRange.location):
117 r.length += len(text) - intersection.length
117 r.length += len(text) - intersection.length
118 else:
118 else:
119 r.length -= intersection.length
119 r.length -= intersection.length
120
120
121
121
122 def update_ranges_for_deletion(self, textRange):
122 def update_ranges_for_deletion(self, textRange):
123 """Update ranges for text deletion at textRange"""
123 """Update ranges for text deletion at textRange"""
124
124
125 for r in [self.inputPromptRange,self.inputRange,
125 for r in [self.inputPromptRange,self.inputRange,
126 self.outputPromptRange, self.outputRange]:
126 self.outputPromptRange, self.outputRange]:
127 if(r==None):
127 if(r==None):
128 continue
128 continue
129 intersection = NSIntersectionRange(r, textRange)
129 intersection = NSIntersectionRange(r, textRange)
130 if(intersection.length == 0): #ranges don't intersect
130 if(intersection.length == 0): #ranges don't intersect
131 if r.location >= textRange.location:
131 if r.location >= textRange.location:
132 r.location -= textRange.length
132 r.location -= textRange.length
133 else: #ranges intersect
133 else: #ranges intersect
134 if(r.location > textRange.location):
134 if(r.location > textRange.location):
135 offset = intersection.length
135 offset = intersection.length
136 r.length -= offset
136 r.length -= offset
137 r.location += offset
137 r.location += offset
138 elif(r.location == textRange.location):
138 elif(r.location == textRange.location):
139 r.length += intersection.length
139 r.length += intersection.length
140 else:
140 else:
141 r.length -= intersection.length
141 r.length -= intersection.length
142
142
143 def __repr__(self):
143 def __repr__(self):
144 return 'CellBlock('+ str((self.inputPromptRange,
144 return 'CellBlock('+ str((self.inputPromptRange,
145 self.inputRange,
145 self.inputRange,
146 self.outputPromptRange,
146 self.outputPromptRange,
147 self.outputRange)) + ')'
147 self.outputRange)) + ')'
148
148
149
149
150
150
151
151
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
152 class IPythonCocoaController(NSObject, AsyncFrontEndBase):
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
153 userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value))
154 waitingForEngine = objc.ivar().bool()
154 waitingForEngine = objc.ivar().bool()
155 textView = objc.IBOutlet()
155 textView = objc.IBOutlet()
156
156
157 def init(self):
157 def init(self):
158 self = super(IPythonCocoaController, self).init()
158 self = super(IPythonCocoaController, self).init()
159 AsyncFrontEndBase.__init__(self,
159 AsyncFrontEndBase.__init__(self,
160 engine=AutoreleasePoolWrappedThreadedEngineService())
160 engine=AutoreleasePoolWrappedThreadedEngineService())
161 if(self != None):
161 if(self != None):
162 self._common_init()
162 self._common_init()
163
163
164 return self
164 return self
165
165
166 def _common_init(self):
166 def _common_init(self):
167 """_common_init"""
167 """_common_init"""
168
168
169 self.userNS = NSMutableDictionary.dictionary()
169 self.userNS = NSMutableDictionary.dictionary()
170 self.waitingForEngine = False
170 self.waitingForEngine = False
171
171
172 self.lines = {}
172 self.lines = {}
173 self.tabSpaces = 4
173 self.tabSpaces = 4
174 self.tabUsesSpaces = True
174 self.tabUsesSpaces = True
175 self.currentBlockID = self.next_block_ID()
175 self.currentBlockID = self.next_block_ID()
176 self.blockRanges = {} # blockID=>CellBlock
176 self.blockRanges = {} # blockID=>CellBlock
177
177
178
178
179 def awakeFromNib(self):
179 def awakeFromNib(self):
180 """awakeFromNib"""
180 """awakeFromNib"""
181
181
182 self._common_init()
182 self._common_init()
183
183
184 # Start the IPython engine
184 # Start the IPython engine
185 self.engine.startService()
185 self.engine.startService()
186 NSLog('IPython engine started')
186 NSLog('IPython engine started')
187
187
188 # Register for app termination
188 # Register for app termination
189 nc = NSNotificationCenter.defaultCenter()
189 nc = NSNotificationCenter.defaultCenter()
190 nc.addObserver_selector_name_object_(
190 nc.addObserver_selector_name_object_(
191 self,
191 self,
192 'appWillTerminate:',
192 'appWillTerminate:',
193 NSApplicationWillTerminateNotification,
193 NSApplicationWillTerminateNotification,
194 None)
194 None)
195
195
196 self.textView.setDelegate_(self)
196 self.textView.setDelegate_(self)
197 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
197 self.textView.enclosingScrollView().setHasVerticalRuler_(True)
198 r = NSRulerView.alloc().initWithScrollView_orientation_(
198 r = NSRulerView.alloc().initWithScrollView_orientation_(
199 self.textView.enclosingScrollView(),
199 self.textView.enclosingScrollView(),
200 NSVerticalRuler)
200 NSVerticalRuler)
201 self.verticalRulerView = r
201 self.verticalRulerView = r
202 self.verticalRulerView.setClientView_(self.textView)
202 self.verticalRulerView.setClientView_(self.textView)
203 self._start_cli_banner()
203 self._start_cli_banner()
204 self.start_new_block()
204 self.start_new_block()
205
205
206
206
207 def appWillTerminate_(self, notification):
207 def appWillTerminate_(self, notification):
208 """appWillTerminate"""
208 """appWillTerminate"""
209
209
210 self.engine.stopService()
210 self.engine.stopService()
211
211
212
212
213 def complete(self, token):
213 def complete(self, token):
214 """Complete token in engine's user_ns
214 """Complete token in engine's user_ns
215
215
216 Parameters
216 Parameters
217 ----------
217 ----------
218 token : string
218 token : string
219
219
220 Result
220 Result
221 ------
221 ------
222 Deferred result of
222 Deferred result of
223 IPython.kernel.engineservice.IEngineBase.complete
223 IPython.kernel.engineservice.IEngineBase.complete
224 """
224 """
225
225
226 return self.engine.complete(token)
226 return self.engine.complete(token)
227
227
228
228
229 def execute(self, block, blockID=None):
229 def execute(self, block, blockID=None):
230 self.waitingForEngine = True
230 self.waitingForEngine = True
231 self.willChangeValueForKey_('commandHistory')
231 self.willChangeValueForKey_('commandHistory')
232 d = super(IPythonCocoaController, self).execute(block,
232 d = super(IPythonCocoaController, self).execute(block,
233 blockID)
233 blockID)
234 d.addBoth(self._engine_done)
234 d.addBoth(self._engine_done)
235 d.addCallback(self._update_user_ns)
235 d.addCallback(self._update_user_ns)
236
236
237 return d
237 return d
238
238
239
239
240 def push_(self, namespace):
240 def push_(self, namespace):
241 """Push dictionary of key=>values to python namespace"""
241 """Push dictionary of key=>values to python namespace"""
242
242
243 self.waitingForEngine = True
243 self.waitingForEngine = True
244 self.willChangeValueForKey_('commandHistory')
244 self.willChangeValueForKey_('commandHistory')
245 d = self.engine.push(namespace)
245 d = self.engine.push(namespace)
246 d.addBoth(self._engine_done)
246 d.addBoth(self._engine_done)
247 d.addCallback(self._update_user_ns)
247 d.addCallback(self._update_user_ns)
248
248
249
249
250 def pull_(self, keys):
250 def pull_(self, keys):
251 """Pull keys from python namespace"""
251 """Pull keys from python namespace"""
252
252
253 self.waitingForEngine = True
253 self.waitingForEngine = True
254 result = blockingCallFromThread(self.engine.pull, keys)
254 result = blockingCallFromThread(self.engine.pull, keys)
255 self.waitingForEngine = False
255 self.waitingForEngine = False
256
256
257 @objc.signature('v@:@I')
257 @objc.signature('v@:@I')
258 def executeFileAtPath_encoding_(self, path, encoding):
258 def executeFileAtPath_encoding_(self, path, encoding):
259 """Execute file at path in an empty namespace. Update the engine
259 """Execute file at path in an empty namespace. Update the engine
260 user_ns with the resulting locals."""
260 user_ns with the resulting locals."""
261
261
262 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
262 lines,err = NSString.stringWithContentsOfFile_encoding_error_(
263 path,
263 path,
264 encoding,
264 encoding,
265 None)
265 None)
266 self.engine.execute(lines)
266 self.engine.execute(lines)
267
267
268
268
269 def _engine_done(self, x):
269 def _engine_done(self, x):
270 self.waitingForEngine = False
270 self.waitingForEngine = False
271 self.didChangeValueForKey_('commandHistory')
271 self.didChangeValueForKey_('commandHistory')
272 return x
272 return x
273
273
274 def _update_user_ns(self, result):
274 def _update_user_ns(self, result):
275 """Update self.userNS from self.engine's namespace"""
275 """Update self.userNS from self.engine's namespace"""
276 d = self.engine.keys()
276 d = self.engine.keys()
277 d.addCallback(self._get_engine_namespace_values_for_keys)
277 d.addCallback(self._get_engine_namespace_values_for_keys)
278
278
279 return result
279 return result
280
280
281
281
282 def _get_engine_namespace_values_for_keys(self, keys):
282 def _get_engine_namespace_values_for_keys(self, keys):
283 d = self.engine.pull(keys)
283 d = self.engine.pull(keys)
284 d.addCallback(self._store_engine_namespace_values, keys=keys)
284 d.addCallback(self._store_engine_namespace_values, keys=keys)
285
285
286
286
287 def _store_engine_namespace_values(self, values, keys=[]):
287 def _store_engine_namespace_values(self, values, keys=[]):
288 assert(len(values) == len(keys))
288 assert(len(values) == len(keys))
289 self.willChangeValueForKey_('userNS')
289 self.willChangeValueForKey_('userNS')
290 for (k,v) in zip(keys,values):
290 for (k,v) in zip(keys,values):
291 self.userNS[k] = saferepr(v)
291 self.userNS[k] = saferepr(v)
292 self.didChangeValueForKey_('userNS')
292 self.didChangeValueForKey_('userNS')
293
293
294
294
295 def update_cell_prompt(self, result, blockID=None):
295 def update_cell_prompt(self, result, blockID=None):
296 print self.blockRanges
296 print self.blockRanges
297 if(isinstance(result, Failure)):
297 if(isinstance(result, Failure)):
298 prompt = self.input_prompt()
298 prompt = self.input_prompt()
299
299
300 else:
300 else:
301 prompt = self.input_prompt(number=result['number'])
301 prompt = self.input_prompt(number=result['number'])
302
302
303 r = self.blockRanges[blockID].inputPromptRange
303 r = self.blockRanges[blockID].inputPromptRange
304 self.insert_text(prompt,
304 self.insert_text(prompt,
305 textRange=r,
305 textRange=r,
306 scrollToVisible=False
306 scrollToVisible=False
307 )
307 )
308
308
309 return result
309 return result
310
310
311
311
312 def render_result(self, result):
312 def render_result(self, result):
313 blockID = result['blockID']
313 blockID = result['blockID']
314 inputRange = self.blockRanges[blockID].inputRange
314 inputRange = self.blockRanges[blockID].inputRange
315 del self.blockRanges[blockID]
315 del self.blockRanges[blockID]
316
316
317 #print inputRange,self.current_block_range()
317 #print inputRange,self.current_block_range()
318 self.insert_text('\n' +
318 self.insert_text('\n' +
319 self.output_prompt(number=result['number']) +
319 self.output_prompt(number=result['number']) +
320 result.get('display',{}).get('pprint','') +
320 result.get('display',{}).get('pprint','') +
321 '\n\n',
321 '\n\n',
322 textRange=NSMakeRange(inputRange.location+inputRange.length,
322 textRange=NSMakeRange(inputRange.location+inputRange.length,
323 0))
323 0))
324 return result
324 return result
325
325
326
326
327 def render_error(self, failure):
327 def render_error(self, failure):
328 print failure
328 print failure
329 blockID = failure.blockID
329 blockID = failure.blockID
330 inputRange = self.blockRanges[blockID].inputRange
330 inputRange = self.blockRanges[blockID].inputRange
331 self.insert_text('\n' +
331 self.insert_text('\n' +
332 self.output_prompt() +
332 self.output_prompt() +
333 '\n' +
333 '\n' +
334 failure.getErrorMessage() +
334 failure.getErrorMessage() +
335 '\n\n',
335 '\n\n',
336 textRange=NSMakeRange(inputRange.location +
336 textRange=NSMakeRange(inputRange.location +
337 inputRange.length,
337 inputRange.length,
338 0))
338 0))
339 self.start_new_block()
339 self.start_new_block()
340 return failure
340 return failure
341
341
342
342
343 def _start_cli_banner(self):
343 def _start_cli_banner(self):
344 """Print banner"""
344 """Print banner"""
345
345
346 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
346 banner = """IPython1 %s -- An enhanced Interactive Python.""" % \
347 IPython.__version__
347 IPython.__version__
348
348
349 self.insert_text(banner + '\n\n')
349 self.insert_text(banner + '\n\n')
350
350
351
351
352 def start_new_block(self):
352 def start_new_block(self):
353 """"""
353 """"""
354
354
355 self.currentBlockID = self.next_block_ID()
355 self.currentBlockID = self.next_block_ID()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
356 self.blockRanges[self.currentBlockID] = self.new_cell_block()
357 self.insert_text(self.input_prompt(),
357 self.insert_text(self.input_prompt(),
358 textRange=self.current_block_range().inputPromptRange)
358 textRange=self.current_block_range().inputPromptRange)
359
359
360
360
361
361
362 def next_block_ID(self):
362 def next_block_ID(self):
363
363
364 return uuid.uuid4()
364 return guid.generate()
365
365
366 def new_cell_block(self):
366 def new_cell_block(self):
367 """A new CellBlock at the end of self.textView.textStorage()"""
367 """A new CellBlock at the end of self.textView.textStorage()"""
368
368
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
369 return CellBlock(NSMakeRange(self.textView.textStorage().length(),
370 0), #len(self.input_prompt())),
370 0), #len(self.input_prompt())),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
371 NSMakeRange(self.textView.textStorage().length(),# + len(self.input_prompt()),
372 0))
372 0))
373
373
374
374
375 def current_block_range(self):
375 def current_block_range(self):
376 return self.blockRanges.get(self.currentBlockID,
376 return self.blockRanges.get(self.currentBlockID,
377 self.new_cell_block())
377 self.new_cell_block())
378
378
379 def current_block(self):
379 def current_block(self):
380 """The current block's text"""
380 """The current block's text"""
381
381
382 return self.text_for_range(self.current_block_range().inputRange)
382 return self.text_for_range(self.current_block_range().inputRange)
383
383
384 def text_for_range(self, textRange):
384 def text_for_range(self, textRange):
385 """text_for_range"""
385 """text_for_range"""
386
386
387 ts = self.textView.textStorage()
387 ts = self.textView.textStorage()
388 return ts.string().substringWithRange_(textRange)
388 return ts.string().substringWithRange_(textRange)
389
389
390 def current_line(self):
390 def current_line(self):
391 block = self.text_for_range(self.current_block_range().inputRange)
391 block = self.text_for_range(self.current_block_range().inputRange)
392 block = block.split('\n')
392 block = block.split('\n')
393 return block[-1]
393 return block[-1]
394
394
395
395
396 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
396 def insert_text(self, string=None, textRange=None, scrollToVisible=True):
397 """Insert text into textView at textRange, updating blockRanges
397 """Insert text into textView at textRange, updating blockRanges
398 as necessary
398 as necessary
399 """
399 """
400 if(textRange == None):
400 if(textRange == None):
401 #range for end of text
401 #range for end of text
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
402 textRange = NSMakeRange(self.textView.textStorage().length(), 0)
403
403
404
404
405 self.textView.replaceCharactersInRange_withString_(
405 self.textView.replaceCharactersInRange_withString_(
406 textRange, string)
406 textRange, string)
407
407
408 for r in self.blockRanges.itervalues():
408 for r in self.blockRanges.itervalues():
409 r.update_ranges_for_insertion(string, textRange)
409 r.update_ranges_for_insertion(string, textRange)
410
410
411 self.textView.setSelectedRange_(textRange)
411 self.textView.setSelectedRange_(textRange)
412 if(scrollToVisible):
412 if(scrollToVisible):
413 self.textView.scrollRangeToVisible_(textRange)
413 self.textView.scrollRangeToVisible_(textRange)
414
414
415
415
416
416
417 def replace_current_block_with_string(self, textView, string):
417 def replace_current_block_with_string(self, textView, string):
418 textView.replaceCharactersInRange_withString_(
418 textView.replaceCharactersInRange_withString_(
419 self.current_block_range().inputRange,
419 self.current_block_range().inputRange,
420 string)
420 string)
421 self.current_block_range().inputRange.length = len(string)
421 self.current_block_range().inputRange.length = len(string)
422 r = NSMakeRange(textView.textStorage().length(), 0)
422 r = NSMakeRange(textView.textStorage().length(), 0)
423 textView.scrollRangeToVisible_(r)
423 textView.scrollRangeToVisible_(r)
424 textView.setSelectedRange_(r)
424 textView.setSelectedRange_(r)
425
425
426
426
427 def current_indent_string(self):
427 def current_indent_string(self):
428 """returns string for indent or None if no indent"""
428 """returns string for indent or None if no indent"""
429
429
430 return self._indent_for_block(self.current_block())
430 return self._indent_for_block(self.current_block())
431
431
432
432
433 def _indent_for_block(self, block):
433 def _indent_for_block(self, block):
434 lines = block.split('\n')
434 lines = block.split('\n')
435 if(len(lines) > 1):
435 if(len(lines) > 1):
436 currentIndent = len(lines[-1]) - len(lines[-1].lstrip())
436 currentIndent = len(lines[-1]) - len(lines[-1].lstrip())
437 if(currentIndent == 0):
437 if(currentIndent == 0):
438 currentIndent = self.tabSpaces
438 currentIndent = self.tabSpaces
439
439
440 if(self.tabUsesSpaces):
440 if(self.tabUsesSpaces):
441 result = ' ' * currentIndent
441 result = ' ' * currentIndent
442 else:
442 else:
443 result = '\t' * (currentIndent/self.tabSpaces)
443 result = '\t' * (currentIndent/self.tabSpaces)
444 else:
444 else:
445 result = None
445 result = None
446
446
447 return result
447 return result
448
448
449
449
450 # NSTextView delegate methods...
450 # NSTextView delegate methods...
451 def textView_doCommandBySelector_(self, textView, selector):
451 def textView_doCommandBySelector_(self, textView, selector):
452 assert(textView == self.textView)
452 assert(textView == self.textView)
453 NSLog("textView_doCommandBySelector_: "+selector)
453 NSLog("textView_doCommandBySelector_: "+selector)
454
454
455
455
456 if(selector == 'insertNewline:'):
456 if(selector == 'insertNewline:'):
457 indent = self.current_indent_string()
457 indent = self.current_indent_string()
458 if(indent):
458 if(indent):
459 line = indent + self.current_line()
459 line = indent + self.current_line()
460 else:
460 else:
461 line = self.current_line()
461 line = self.current_line()
462
462
463 if(self.is_complete(self.current_block())):
463 if(self.is_complete(self.current_block())):
464 self.execute(self.current_block(),
464 self.execute(self.current_block(),
465 blockID=self.currentBlockID)
465 blockID=self.currentBlockID)
466 self.start_new_block()
466 self.start_new_block()
467
467
468 return True
468 return True
469
469
470 return False
470 return False
471
471
472 elif(selector == 'moveUp:'):
472 elif(selector == 'moveUp:'):
473 prevBlock = self.get_history_previous(self.current_block())
473 prevBlock = self.get_history_previous(self.current_block())
474 if(prevBlock != None):
474 if(prevBlock != None):
475 self.replace_current_block_with_string(textView, prevBlock)
475 self.replace_current_block_with_string(textView, prevBlock)
476 else:
476 else:
477 NSBeep()
477 NSBeep()
478 return True
478 return True
479
479
480 elif(selector == 'moveDown:'):
480 elif(selector == 'moveDown:'):
481 nextBlock = self.get_history_next()
481 nextBlock = self.get_history_next()
482 if(nextBlock != None):
482 if(nextBlock != None):
483 self.replace_current_block_with_string(textView, nextBlock)
483 self.replace_current_block_with_string(textView, nextBlock)
484 else:
484 else:
485 NSBeep()
485 NSBeep()
486 return True
486 return True
487
487
488 elif(selector == 'moveToBeginningOfParagraph:'):
488 elif(selector == 'moveToBeginningOfParagraph:'):
489 textView.setSelectedRange_(NSMakeRange(
489 textView.setSelectedRange_(NSMakeRange(
490 self.current_block_range().inputRange.location,
490 self.current_block_range().inputRange.location,
491 0))
491 0))
492 return True
492 return True
493 elif(selector == 'moveToEndOfParagraph:'):
493 elif(selector == 'moveToEndOfParagraph:'):
494 textView.setSelectedRange_(NSMakeRange(
494 textView.setSelectedRange_(NSMakeRange(
495 self.current_block_range().inputRange.location + \
495 self.current_block_range().inputRange.location + \
496 self.current_block_range().inputRange.length, 0))
496 self.current_block_range().inputRange.length, 0))
497 return True
497 return True
498 elif(selector == 'deleteToEndOfParagraph:'):
498 elif(selector == 'deleteToEndOfParagraph:'):
499 if(textView.selectedRange().location <= \
499 if(textView.selectedRange().location <= \
500 self.current_block_range().location):
500 self.current_block_range().location):
501 raise NotImplemented()
501 raise NotImplemented()
502
502
503 return False # don't actually handle the delete
503 return False # don't actually handle the delete
504
504
505 elif(selector == 'insertTab:'):
505 elif(selector == 'insertTab:'):
506 if(len(self.current_line().strip()) == 0): #only white space
506 if(len(self.current_line().strip()) == 0): #only white space
507 return False
507 return False
508 else:
508 else:
509 self.textView.complete_(self)
509 self.textView.complete_(self)
510 return True
510 return True
511
511
512 elif(selector == 'deleteBackward:'):
512 elif(selector == 'deleteBackward:'):
513 #if we're at the beginning of the current block, ignore
513 #if we're at the beginning of the current block, ignore
514 if(textView.selectedRange().location == \
514 if(textView.selectedRange().location == \
515 self.current_block_range().inputRange.location):
515 self.current_block_range().inputRange.location):
516 return True
516 return True
517 else:
517 else:
518 for r in self.blockRanges.itervalues():
518 for r in self.blockRanges.itervalues():
519 deleteRange = textView.selectedRange
519 deleteRange = textView.selectedRange
520 if(deleteRange.length == 0):
520 if(deleteRange.length == 0):
521 deleteRange.location -= 1
521 deleteRange.location -= 1
522 deleteRange.length = 1
522 deleteRange.length = 1
523 r.update_ranges_for_deletion(deleteRange)
523 r.update_ranges_for_deletion(deleteRange)
524 return False
524 return False
525 return False
525 return False
526
526
527
527
528 def textView_shouldChangeTextInRanges_replacementStrings_(self,
528 def textView_shouldChangeTextInRanges_replacementStrings_(self,
529 textView, ranges, replacementStrings):
529 textView, ranges, replacementStrings):
530 """
530 """
531 Delegate method for NSTextView.
531 Delegate method for NSTextView.
532
532
533 Refuse change text in ranges not at end, but make those changes at
533 Refuse change text in ranges not at end, but make those changes at
534 end.
534 end.
535 """
535 """
536
536
537 assert(len(ranges) == len(replacementStrings))
537 assert(len(ranges) == len(replacementStrings))
538 allow = True
538 allow = True
539 for r,s in zip(ranges, replacementStrings):
539 for r,s in zip(ranges, replacementStrings):
540 r = r.rangeValue()
540 r = r.rangeValue()
541 if(textView.textStorage().length() > 0 and
541 if(textView.textStorage().length() > 0 and
542 r.location < self.current_block_range().inputRange.location):
542 r.location < self.current_block_range().inputRange.location):
543 self.insert_text(s)
543 self.insert_text(s)
544 allow = False
544 allow = False
545
545
546 return allow
546 return allow
547
547
548 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
548 def textView_completions_forPartialWordRange_indexOfSelectedItem_(self,
549 textView, words, charRange, index):
549 textView, words, charRange, index):
550 try:
550 try:
551 ts = textView.textStorage()
551 ts = textView.textStorage()
552 token = ts.string().substringWithRange_(charRange)
552 token = ts.string().substringWithRange_(charRange)
553 completions = blockingCallFromThread(self.complete, token)
553 completions = blockingCallFromThread(self.complete, token)
554 except:
554 except:
555 completions = objc.nil
555 completions = objc.nil
556 NSBeep()
556 NSBeep()
557
557
558 return (completions,0)
558 return (completions,0)
559
559
560
560
@@ -1,431 +1,343 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
3 """
3 """
4 frontendbase provides an interface and base class for GUI frontends for
4 frontendbase provides an interface and base class for GUI frontends for
5 IPython.kernel/IPython.kernel.core.
5 IPython.kernel/IPython.kernel.core.
6
6
7 Frontend implementations will likely want to subclass FrontEndBase.
7 Frontend implementations will likely want to subclass FrontEndBase.
8
8
9 Author: Barry Wark
9 Author: Barry Wark
10 """
10 """
11 __docformat__ = "restructuredtext en"
11 __docformat__ = "restructuredtext en"
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Copyright (C) 2008 The IPython Development Team
14 # Copyright (C) 2008 The IPython Development Team
15 #
15 #
16 # Distributed under the terms of the BSD License. The full license is in
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
17 # the file COPYING, distributed as part of this software.
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19
19
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 import string
23 import string
24
24 import codeop
25 try:
25 from IPython.external import guid
26 import _ast
27 except ImportError:
28 # Python 2.4 hackish workaround.
29 class bunch: pass
30 _ast = bunch()
31 _ast.PyCF_ONLY_AST = 1024
32
33
34
35 try:
36 import uuid
37 except ImportError:
38 # Python 2.4 hackish workaround.
39 class UUID:
40 def __init__(self,bytes):
41 version = 4
42 int = long(('%02x'*16) % tuple(map(ord, bytes)), 16)
43 # Set the variant to RFC 4122.
44 int &= ~(0xc000 << 48L)
45 int |= 0x8000 << 48L
46 # Set the version number.
47 int &= ~(0xf000 << 64L)
48 int |= version << 76L
49 self.__dict__['int'] = int
50
51 def __cmp__(self, other):
52 if isinstance(other, UUID):
53 return cmp(self.int, other.int)
54 return NotImplemented
55
56 def __hash__(self):
57 return hash(self.int)
58
59 def __int__(self):
60 return self.int
61
62 def __repr__(self):
63 return 'UUID(%r)' % str(self)
64
65 def __setattr__(self, name, value):
66 raise TypeError('UUID objects are immutable')
67
68 def __str__(self):
69 hex = '%032x' % self.int
70 return '%s-%s-%s-%s-%s' % (
71 hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
72
73 def get_bytes(self):
74 bytes = ''
75 for shift in range(0, 128, 8):
76 bytes = chr((self.int >> shift) & 0xff) + bytes
77 return bytes
78
79 bytes = property(get_bytes)
80
81
82 def _u4():
83 "Fake random uuid"
84
85 import random
86 bytes = [chr(random.randrange(256)) for i in range(16)]
87 return UUID(bytes)
88
89 class bunch: pass
90 uuid = bunch()
91 uuid.uuid4 = _u4
92 del _u4
93
94
26
95
27
96 from IPython.frontend.zopeinterface import (
28 from IPython.frontend.zopeinterface import (
97 Interface,
29 Interface,
98 Attribute,
30 Attribute,
99 implements,
100 classProvides
101 )
31 )
102 from IPython.kernel.core.history import FrontEndHistory
32 from IPython.kernel.core.history import FrontEndHistory
103 from IPython.kernel.core.util import Bunch
33 from IPython.kernel.core.util import Bunch
104
34
105 ##############################################################################
35 ##############################################################################
106 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
36 # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or
107 # not
37 # not
108
38
109 rc = Bunch()
39 rc = Bunch()
110 rc.prompt_in1 = r'In [$number]: '
40 rc.prompt_in1 = r'In [$number]: '
111 rc.prompt_in2 = r'...'
41 rc.prompt_in2 = r'...'
112 rc.prompt_out = r'Out [$number]: '
42 rc.prompt_out = r'Out [$number]: '
113
43
114 ##############################################################################
44 ##############################################################################
115 # Interface definitions
45 # Interface definitions
116 ##############################################################################
46 ##############################################################################
117
47
118 class IFrontEndFactory(Interface):
48 class IFrontEndFactory(Interface):
119 """Factory interface for frontends."""
49 """Factory interface for frontends."""
120
50
121 def __call__(engine=None, history=None):
51 def __call__(engine=None, history=None):
122 """
52 """
123 Parameters:
53 Parameters:
124 interpreter : IPython.kernel.engineservice.IEngineCore
54 interpreter : IPython.kernel.engineservice.IEngineCore
125 """
55 """
126
56
127 pass
57 pass
128
58
129
59
130 class IFrontEnd(Interface):
60 class IFrontEnd(Interface):
131 """Interface for frontends. All methods return t.i.d.Deferred"""
61 """Interface for frontends. All methods return t.i.d.Deferred"""
132
62
133 Attribute("input_prompt_template", "string.Template instance\
63 Attribute("input_prompt_template", "string.Template instance\
134 substituteable with execute result.")
64 substituteable with execute result.")
135 Attribute("output_prompt_template", "string.Template instance\
65 Attribute("output_prompt_template", "string.Template instance\
136 substituteable with execute result.")
66 substituteable with execute result.")
137 Attribute("continuation_prompt_template", "string.Template instance\
67 Attribute("continuation_prompt_template", "string.Template instance\
138 substituteable with execute result.")
68 substituteable with execute result.")
139
69
140 def update_cell_prompt(result, blockID=None):
70 def update_cell_prompt(result, blockID=None):
141 """Subclass may override to update the input prompt for a block.
71 """Subclass may override to update the input prompt for a block.
142
72
143 In asynchronous frontends, this method will be called as a
73 In asynchronous frontends, this method will be called as a
144 twisted.internet.defer.Deferred's callback/errback.
74 twisted.internet.defer.Deferred's callback/errback.
145 Implementations should thus return result when finished.
75 Implementations should thus return result when finished.
146
76
147 Result is a result dict in case of success, and a
77 Result is a result dict in case of success, and a
148 twisted.python.util.failure.Failure in case of an error
78 twisted.python.util.failure.Failure in case of an error
149 """
79 """
150
80
151 pass
81 pass
152
82
153 def render_result(result):
83 def render_result(result):
154 """Render the result of an execute call. Implementors may choose the
84 """Render the result of an execute call. Implementors may choose the
155 method of rendering.
85 method of rendering.
156 For example, a notebook-style frontend might render a Chaco plot
86 For example, a notebook-style frontend might render a Chaco plot
157 inline.
87 inline.
158
88
159 Parameters:
89 Parameters:
160 result : dict (result of IEngineBase.execute )
90 result : dict (result of IEngineBase.execute )
161 blockID = result['blockID']
91 blockID = result['blockID']
162
92
163 Result:
93 Result:
164 Output of frontend rendering
94 Output of frontend rendering
165 """
95 """
166
96
167 pass
97 pass
168
98
169 def render_error(failure):
99 def render_error(failure):
170 """Subclasses must override to render the failure.
100 """Subclasses must override to render the failure.
171
101
172 In asynchronous frontend, since this method will be called as a
102 In asynchronous frontend, since this method will be called as a
173 twisted.internet.defer.Deferred's callback. Implementations
103 twisted.internet.defer.Deferred's callback. Implementations
174 should thus return result when finished.
104 should thus return result when finished.
175
105
176 blockID = failure.blockID
106 blockID = failure.blockID
177 """
107 """
178
108
179 pass
109 pass
180
110
181 def input_prompt(number=''):
111 def input_prompt(number=''):
182 """Returns the input prompt by subsituting into
112 """Returns the input prompt by subsituting into
183 self.input_prompt_template
113 self.input_prompt_template
184 """
114 """
185 pass
115 pass
186
116
187 def output_prompt(number=''):
117 def output_prompt(number=''):
188 """Returns the output prompt by subsituting into
118 """Returns the output prompt by subsituting into
189 self.output_prompt_template
119 self.output_prompt_template
190 """
120 """
191
121
192 pass
122 pass
193
123
194 def continuation_prompt():
124 def continuation_prompt():
195 """Returns the continuation prompt by subsituting into
125 """Returns the continuation prompt by subsituting into
196 self.continuation_prompt_template
126 self.continuation_prompt_template
197 """
127 """
198
128
199 pass
129 pass
200
130
201 def is_complete(block):
131 def is_complete(block):
202 """Returns True if block is complete, False otherwise."""
132 """Returns True if block is complete, False otherwise."""
203
133
204 pass
134 pass
205
135
206 def compile_ast(block):
207 """Compiles block to an _ast.AST"""
208
209 pass
210
136
211 def get_history_previous(current_block):
137 def get_history_previous(current_block):
212 """Returns the block previous in the history. Saves currentBlock if
138 """Returns the block previous in the history. Saves currentBlock if
213 the history_cursor is currently at the end of the input history"""
139 the history_cursor is currently at the end of the input history"""
214 pass
140 pass
215
141
216 def get_history_next():
142 def get_history_next():
217 """Returns the next block in the history."""
143 """Returns the next block in the history."""
218
144
219 pass
145 pass
220
146
221 def complete(self, line):
147 def complete(self, line):
222 """Returns the list of possible completions, and the completed
148 """Returns the list of possible completions, and the completed
223 line.
149 line.
224
150
225 The input argument is the full line to be completed. This method
151 The input argument is the full line to be completed. This method
226 returns both the line completed as much as possible, and the list
152 returns both the line completed as much as possible, and the list
227 of further possible completions (full words).
153 of further possible completions (full words).
228 """
154 """
229 pass
155 pass
230
156
231
157
232 ##############################################################################
158 ##############################################################################
233 # Base class for all the frontends.
159 # Base class for all the frontends.
234 ##############################################################################
160 ##############################################################################
235
161
236 class FrontEndBase(object):
162 class FrontEndBase(object):
237 """
163 """
238 FrontEndBase manages the state tasks for a CLI frontend:
164 FrontEndBase manages the state tasks for a CLI frontend:
239 - Input and output history management
165 - Input and output history management
240 - Input/continuation and output prompt generation
166 - Input/continuation and output prompt generation
241
167
242 Some issues (due to possibly unavailable engine):
168 Some issues (due to possibly unavailable engine):
243 - How do we get the current cell number for the engine?
169 - How do we get the current cell number for the engine?
244 - How do we handle completions?
170 - How do we handle completions?
245 """
171 """
246
172
247 history_cursor = 0
173 history_cursor = 0
248
174
249 input_prompt_template = string.Template(rc.prompt_in1)
175 input_prompt_template = string.Template(rc.prompt_in1)
250 output_prompt_template = string.Template(rc.prompt_out)
176 output_prompt_template = string.Template(rc.prompt_out)
251 continuation_prompt_template = string.Template(rc.prompt_in2)
177 continuation_prompt_template = string.Template(rc.prompt_in2)
252
178
253 def __init__(self, shell=None, history=None):
179 def __init__(self, shell=None, history=None):
254 self.shell = shell
180 self.shell = shell
255 if history is None:
181 if history is None:
256 self.history = FrontEndHistory(input_cache=[''])
182 self.history = FrontEndHistory(input_cache=[''])
257 else:
183 else:
258 self.history = history
184 self.history = history
259
185
260
186
261 def input_prompt(self, number=''):
187 def input_prompt(self, number=''):
262 """Returns the current input prompt
188 """Returns the current input prompt
263
189
264 It would be great to use ipython1.core.prompts.Prompt1 here
190 It would be great to use ipython1.core.prompts.Prompt1 here
265 """
191 """
266 return self.input_prompt_template.safe_substitute({'number':number})
192 return self.input_prompt_template.safe_substitute({'number':number})
267
193
268
194
269 def continuation_prompt(self):
195 def continuation_prompt(self):
270 """Returns the current continuation prompt"""
196 """Returns the current continuation prompt"""
271
197
272 return self.continuation_prompt_template.safe_substitute()
198 return self.continuation_prompt_template.safe_substitute()
273
199
274 def output_prompt(self, number=''):
200 def output_prompt(self, number=''):
275 """Returns the output prompt for result"""
201 """Returns the output prompt for result"""
276
202
277 return self.output_prompt_template.safe_substitute({'number':number})
203 return self.output_prompt_template.safe_substitute({'number':number})
278
204
279
205
280 def is_complete(self, block):
206 def is_complete(self, block):
281 """Determine if block is complete.
207 """Determine if block is complete.
282
208
283 Parameters
209 Parameters
284 block : string
210 block : string
285
211
286 Result
212 Result
287 True if block can be sent to the engine without compile errors.
213 True if block can be sent to the engine without compile errors.
288 False otherwise.
214 False otherwise.
289 """
215 """
290
216
291 try:
217 try:
292 ast = self.compile_ast(block)
218 is_complete = codeop.compile_command(block.rstrip() + '\n\n',
219 "<string>", "exec")
293 except:
220 except:
294 return False
221 return False
295
222
296 lines = block.split('\n')
223 lines = block.split('\n')
297 return (len(lines)==1 or str(lines[-1])=='')
224 return ((is_complete is not None)
298
225 and (len(lines)==1 or str(lines[-1])==''))
299
300 def compile_ast(self, block):
301 """Compile block to an AST
302
303 Parameters:
304 block : str
305
306 Result:
307 AST
308
309 Throws:
310 Exception if block cannot be compiled
311 """
312
313 return compile(block, "<string>", "exec", _ast.PyCF_ONLY_AST)
314
226
315
227
316 def execute(self, block, blockID=None):
228 def execute(self, block, blockID=None):
317 """Execute the block and return the result.
229 """Execute the block and return the result.
318
230
319 Parameters:
231 Parameters:
320 block : {str, AST}
232 block : {str, AST}
321 blockID : any
233 blockID : any
322 Caller may provide an ID to identify this block.
234 Caller may provide an ID to identify this block.
323 result['blockID'] := blockID
235 result['blockID'] := blockID
324
236
325 Result:
237 Result:
326 Deferred result of self.interpreter.execute
238 Deferred result of self.interpreter.execute
327 """
239 """
328
240
329 if(not self.is_complete(block)):
241 if(not self.is_complete(block)):
330 raise Exception("Block is not compilable")
242 raise Exception("Block is not compilable")
331
243
332 if(blockID == None):
244 if(blockID == None):
333 blockID = uuid.uuid4() #random UUID
245 blockID = guid.generate()
334
246
335 try:
247 try:
336 result = self.shell.execute(block)
248 result = self.shell.execute(block)
337 except Exception,e:
249 except Exception,e:
338 e = self._add_block_id_for_failure(e, blockID=blockID)
250 e = self._add_block_id_for_failure(e, blockID=blockID)
339 e = self.update_cell_prompt(e, blockID=blockID)
251 e = self.update_cell_prompt(e, blockID=blockID)
340 e = self.render_error(e)
252 e = self.render_error(e)
341 else:
253 else:
342 result = self._add_block_id_for_result(result, blockID=blockID)
254 result = self._add_block_id_for_result(result, blockID=blockID)
343 result = self.update_cell_prompt(result, blockID=blockID)
255 result = self.update_cell_prompt(result, blockID=blockID)
344 result = self.render_result(result)
256 result = self.render_result(result)
345
257
346 return result
258 return result
347
259
348
260
349 def _add_block_id_for_result(self, result, blockID):
261 def _add_block_id_for_result(self, result, blockID):
350 """Add the blockID to result or failure. Unfortunatley, we have to
262 """Add the blockID to result or failure. Unfortunatley, we have to
351 treat failures differently than result dicts.
263 treat failures differently than result dicts.
352 """
264 """
353
265
354 result['blockID'] = blockID
266 result['blockID'] = blockID
355
267
356 return result
268 return result
357
269
358 def _add_block_id_for_failure(self, failure, blockID):
270 def _add_block_id_for_failure(self, failure, blockID):
359 """_add_block_id_for_failure"""
271 """_add_block_id_for_failure"""
360 failure.blockID = blockID
272 failure.blockID = blockID
361 return failure
273 return failure
362
274
363
275
364 def _add_history(self, result, block=None):
276 def _add_history(self, result, block=None):
365 """Add block to the history"""
277 """Add block to the history"""
366
278
367 assert(block != None)
279 assert(block != None)
368 self.history.add_items([block])
280 self.history.add_items([block])
369 self.history_cursor += 1
281 self.history_cursor += 1
370
282
371 return result
283 return result
372
284
373
285
374 def get_history_previous(self, current_block):
286 def get_history_previous(self, current_block):
375 """ Returns previous history string and decrement history cursor.
287 """ Returns previous history string and decrement history cursor.
376 """
288 """
377 command = self.history.get_history_item(self.history_cursor - 1)
289 command = self.history.get_history_item(self.history_cursor - 1)
378
290
379 if command is not None:
291 if command is not None:
380 if(self.history_cursor+1 == len(self.history.input_cache)):
292 if(self.history_cursor+1 == len(self.history.input_cache)):
381 self.history.input_cache[self.history_cursor] = current_block
293 self.history.input_cache[self.history_cursor] = current_block
382 self.history_cursor -= 1
294 self.history_cursor -= 1
383 return command
295 return command
384
296
385
297
386 def get_history_next(self):
298 def get_history_next(self):
387 """ Returns next history string and increment history cursor.
299 """ Returns next history string and increment history cursor.
388 """
300 """
389 command = self.history.get_history_item(self.history_cursor+1)
301 command = self.history.get_history_item(self.history_cursor+1)
390
302
391 if command is not None:
303 if command is not None:
392 self.history_cursor += 1
304 self.history_cursor += 1
393 return command
305 return command
394
306
395 ###
307 ###
396 # Subclasses probably want to override these methods...
308 # Subclasses probably want to override these methods...
397 ###
309 ###
398
310
399 def update_cell_prompt(self, result, blockID=None):
311 def update_cell_prompt(self, result, blockID=None):
400 """Subclass may override to update the input prompt for a block.
312 """Subclass may override to update the input prompt for a block.
401
313
402 This method only really makes sens in asyncrhonous frontend.
314 This method only really makes sens in asyncrhonous frontend.
403 Since this method will be called as a
315 Since this method will be called as a
404 twisted.internet.defer.Deferred's callback, implementations should
316 twisted.internet.defer.Deferred's callback, implementations should
405 return result when finished.
317 return result when finished.
406 """
318 """
407
319
408 raise NotImplementedError
320 raise NotImplementedError
409
321
410
322
411 def render_result(self, result):
323 def render_result(self, result):
412 """Subclasses must override to render result.
324 """Subclasses must override to render result.
413
325
414 In asynchronous frontends, this method will be called as a
326 In asynchronous frontends, this method will be called as a
415 twisted.internet.defer.Deferred's callback. Implementations
327 twisted.internet.defer.Deferred's callback. Implementations
416 should thus return result when finished.
328 should thus return result when finished.
417 """
329 """
418
330
419 raise NotImplementedError
331 raise NotImplementedError
420
332
421
333
422 def render_error(self, failure):
334 def render_error(self, failure):
423 """Subclasses must override to render the failure.
335 """Subclasses must override to render the failure.
424
336
425 In asynchronous frontends, this method will be called as a
337 In asynchronous frontends, this method will be called as a
426 twisted.internet.defer.Deferred's callback. Implementations
338 twisted.internet.defer.Deferred's callback. Implementations
427 should thus return result when finished.
339 should thus return result when finished.
428 """
340 """
429
341
430 raise NotImplementedError
342 raise NotImplementedError
431
343
@@ -1,155 +1,32 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2 """
3 """This file contains unittests for the frontendbase module."""
3 Test the basic functionality of frontendbase.
4 """
4
5
5 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
6
7
7 #---------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
9 #
10 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is
11 # the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
12 #---------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
13
14 #---------------------------------------------------------------------------
15 # Imports
16 #---------------------------------------------------------------------------
17
18 import unittest
19
20 try:
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27
28 from IPython.testing.decorators import skip
29
30 class FrontEndCallbackChecker(AsyncFrontEndBase):
31 """FrontEndBase subclass for checking callbacks"""
32 def __init__(self, engine=None, history=None):
33 super(FrontEndCallbackChecker, self).__init__(engine=engine,
34 history=history)
35 self.updateCalled = False
36 self.renderResultCalled = False
37 self.renderErrorCalled = False
38
39 def update_cell_prompt(self, result, blockID=None):
40 self.updateCalled = True
41 return result
42
43 def render_result(self, result):
44 self.renderResultCalled = True
45 return result
46
47
48 def render_error(self, failure):
49 self.renderErrorCalled = True
50 return failure
51
52
53
54
55 class TestAsyncFrontendBase(unittest.TestCase):
56 def setUp(self):
57 """Setup the EngineService and FrontEndBase"""
58
59 self.fb = FrontEndCallbackChecker(engine=EngineService())
60
61 def test_implements_IFrontEnd(self):
62 assert(frontendbase.IFrontEnd.implementedBy(
63 AsyncFrontEndBase))
64
65 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
67
68 block = """def test(a):"""
69
70 assert(self.fb.is_complete(block) == False)
71
72 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
74
75 block = """def test(a): pass"""
76
77 assert(self.fb.is_complete(block))
78
79 block = """a=3"""
80
14
81 assert(self.fb.is_complete(block))
15 from IPython.frontend.frontendbase import FrontEndBase
82
16
83 def test_blockID_added_to_result(self):
17 def test_iscomplete():
84 block = """3+3"""
18 """ Check that is_complete works.
85
86 d = self.fb.execute(block, blockID='TEST_ID')
87
88 d.addCallback(self.checkBlockID, expected='TEST_ID')
89
90 def test_blockID_added_to_failure(self):
91 block = "raise Exception()"
92
93 d = self.fb.execute(block,blockID='TEST_ID')
94 d.addErrback(self.checkFailureID, expected='TEST_ID')
95
96 def checkBlockID(self, result, expected=""):
97 assert(result['blockID'] == expected)
98
99
100 def checkFailureID(self, failure, expected=""):
101 assert(failure.blockID == expected)
102
103
104 def test_callbacks_added_to_execute(self):
105 """test that
106 update_cell_prompt
107 render_result
108
109 are added to execute request
110 """
19 """
111
20 f = FrontEndBase()
112 d = self.fb.execute("10+10")
21 assert f.is_complete('(a + a)')
113 d.addCallback(self.checkCallbacks)
22 assert not f.is_complete('(a + a')
114
23 assert f.is_complete('1')
115 def checkCallbacks(self, result):
24 assert not f.is_complete('1 + ')
116 assert(self.fb.updateCalled)
25 assert not f.is_complete('1 + \n\n')
117 assert(self.fb.renderResultCalled)
26 assert f.is_complete('if True:\n print 1\n')
118
27 assert not f.is_complete('if True:\n print 1')
119 @skip("This test fails and lead to an unhandled error in a Deferred.")
28 assert f.is_complete('def f():\n print 1\n')
120 def test_error_callback_added_to_execute(self):
29
121 """test that render_error called on execution error"""
30 if __name__ == '__main__':
122
31 test_iscomplete()
123 d = self.fb.execute("raise Exception()")
124 d.addCallback(self.checkRenderError)
125
126 def checkRenderError(self, result):
127 assert(self.fb.renderErrorCalled)
128
129 def test_history_returns_expected_block(self):
130 """Make sure history browsing doesn't fail"""
131
132 blocks = ["a=1","a=2","a=3"]
133 for b in blocks:
134 d = self.fb.execute(b)
135
136 # d is now the deferred for the last executed block
137 d.addCallback(self.historyTests, blocks)
138
139
140 def historyTests(self, result, blocks):
141 """historyTests"""
142
143 assert(len(blocks) >= 3)
144 assert(self.fb.get_history_previous("") == blocks[-2])
145 assert(self.fb.get_history_previous("") == blocks[-3])
146 assert(self.fb.get_history_next() == blocks[-2])
147
148
149 def test_history_returns_none_at_startup(self):
150 """test_history_returns_none_at_startup"""
151
152 assert(self.fb.get_history_previous("")==None)
153 assert(self.fb.get_history_next()==None)
154
155
32
@@ -1,30 +1,27 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
2 # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*-
3 """
3 """
4 zope.interface mock. If zope is installed, this module provides a zope
4 zope.interface mock. If zope is installed, this module provides a zope
5 interface classes, if not it provides mocks for them.
5 interface classes, if not it provides mocks for them.
6
6
7 Classes provided:
7 Classes provided:
8 Interface, Attribute, implements, classProvides
8 Interface, Attribute, implements, classProvides
9 """
9 """
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
20 # Imports
21 #-------------------------------------------------------------------------------
22 try:
19 try:
23 from zope.interface import Interface, Attribute, implements, classProvides
20 from zope.interface import Interface, Attribute, implements, classProvides
24 except ImportError:
21 except ImportError:
25 #zope.interface is not available
22 #zope.interface is not available
26 Interface = object
23 Interface = object
27 def Attribute(name, doc): pass
24 def Attribute(name, doc): pass
28 def implements(interface): pass
25 def implements(interface): pass
29 def classProvides(interface): pass
26 def classProvides(interface): pass
30
27
@@ -1,68 +1,68 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Test the output capture at the OS level, using file descriptors.
3 Test the output capture at the OS level, using file descriptors.
4 """
4 """
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15
15
16 import os
16 import os
17 from cStringIO import StringIO
17 from cStringIO import StringIO
18
18
19 from IPython.testing import decorators as testdec
19 # FIXME:
20 import nose
21 import sys
22 if sys.platform == 'win32':
23 raise nose.SkipTest("These tests are not reliable under windows")
20
24
21 # FIXME
22 @testdec.skip("This doesn't work under Windows")
23 def test_redirector():
25 def test_redirector():
24 """ Checks that the redirector can be used to do synchronous capture.
26 """ Checks that the redirector can be used to do synchronous capture.
25 """
27 """
26 from IPython.kernel.core.fd_redirector import FDRedirector
28 from IPython.kernel.core.fd_redirector import FDRedirector
27 r = FDRedirector()
29 r = FDRedirector()
28 out = StringIO()
30 out = StringIO()
29 try:
31 try:
30 r.start()
32 r.start()
31 for i in range(10):
33 for i in range(10):
32 os.system('echo %ic' % i)
34 os.system('echo %ic' % i)
33 print >>out, r.getvalue(),
35 print >>out, r.getvalue(),
34 print >>out, i
36 print >>out, i
35 except:
37 except:
36 r.stop()
38 r.stop()
37 raise
39 raise
38 r.stop()
40 r.stop()
39 result1 = out.getvalue()
41 result1 = out.getvalue()
40 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
42 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
41 assert result1 == result2
43 assert result1 == result2
42
44
43 # FIXME
44 @testdec.skip("This doesn't work under Windows")
45 def test_redirector_output_trap():
45 def test_redirector_output_trap():
46 """ This test check not only that the redirector_output_trap does
46 """ This test check not only that the redirector_output_trap does
47 trap the output, but also that it does it in a gready way, that
47 trap the output, but also that it does it in a gready way, that
48 is by calling the callback ASAP.
48 is by calling the callback ASAP.
49 """
49 """
50 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
50 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
51 out = StringIO()
51 out = StringIO()
52 trap = RedirectorOutputTrap(out.write, out.write)
52 trap = RedirectorOutputTrap(out.write, out.write)
53 try:
53 try:
54 trap.set()
54 trap.set()
55 for i in range(10):
55 for i in range(10):
56 os.system('echo %ic' % i)
56 os.system('echo %ic' % i)
57 print "%ip" % i
57 print "%ip" % i
58 print >>out, i
58 print >>out, i
59 except:
59 except:
60 trap.unset()
60 trap.unset()
61 raise
61 raise
62 trap.unset()
62 trap.unset()
63 result1 = out.getvalue()
63 result1 = out.getvalue()
64 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
64 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
65 assert result1 == result2
65 assert result1 == result2
66
66
67
67
68
68
@@ -1,11 +1,11 b''
1 ===============
1 ==============
2 IPython1 README
2 IPython README
3 ===============
3 ==============
4
5 .. contents::
6
4
7 Overview
5 Overview
8 ========
6 ========
9
7
10 Welcome to IPython. New users should consult our documentation, which can be found
8 Welcome to IPython. Our documentation can be found in the docs/source
11 in the docs/source subdirectory.
9 subdirectory. We also have ``.html`` and ``.pdf`` versions of this
10 documentation available on the IPython `website <http://ipython.scipy.org>`_.
11
@@ -1,233 +1,233 b''
1 .. _overview:
1 .. _overview:
2
2
3 ============
3 ============
4 Introduction
4 Introduction
5 ============
5 ============
6
6
7 Overview
7 Overview
8 ========
8 ========
9
9
10 One of Python's most useful features is its interactive interpreter.
10 One of Python's most useful features is its interactive interpreter.
11 This system allows very fast testing of ideas without the overhead of
11 This system allows very fast testing of ideas without the overhead of
12 creating test files as is typical in most programming languages.
12 creating test files as is typical in most programming languages.
13 However, the interpreter supplied with the standard Python distribution
13 However, the interpreter supplied with the standard Python distribution
14 is somewhat limited for extended interactive use.
14 is somewhat limited for extended interactive use.
15
15
16 The goal of IPython is to create a comprehensive environment for
16 The goal of IPython is to create a comprehensive environment for
17 interactive and exploratory computing. To support, this goal, IPython
17 interactive and exploratory computing. To support this goal, IPython
18 has two main components:
18 has two main components:
19
19
20 * An enhanced interactive Python shell.
20 * An enhanced interactive Python shell.
21 * An architecture for interactive parallel computing.
21 * An architecture for interactive parallel computing.
22
22
23 All of IPython is open source (released under the revised BSD license).
23 All of IPython is open source (released under the revised BSD license).
24
24
25 Enhanced interactive Python shell
25 Enhanced interactive Python shell
26 =================================
26 =================================
27
27
28 IPython's interactive shell (:command:`ipython`), has the following goals,
28 IPython's interactive shell (:command:`ipython`), has the following goals,
29 amongst others:
29 amongst others:
30
30
31 1. Provide an interactive shell superior to Python's default. IPython
31 1. Provide an interactive shell superior to Python's default. IPython
32 has many features for object introspection, system shell access,
32 has many features for object introspection, system shell access,
33 and its own special command system for adding functionality when
33 and its own special command system for adding functionality when
34 working interactively. It tries to be a very efficient environment
34 working interactively. It tries to be a very efficient environment
35 both for Python code development and for exploration of problems
35 both for Python code development and for exploration of problems
36 using Python objects (in situations like data analysis).
36 using Python objects (in situations like data analysis).
37
37
38 2. Serve as an embeddable, ready to use interpreter for your own
38 2. Serve as an embeddable, ready to use interpreter for your own
39 programs. IPython can be started with a single call from inside
39 programs. IPython can be started with a single call from inside
40 another program, providing access to the current namespace. This
40 another program, providing access to the current namespace. This
41 can be very useful both for debugging purposes and for situations
41 can be very useful both for debugging purposes and for situations
42 where a blend of batch-processing and interactive exploration are
42 where a blend of batch-processing and interactive exploration are
43 needed. New in the 0.9 version of IPython is a reusable wxPython
43 needed. New in the 0.9 version of IPython is a reusable wxPython
44 based IPython widget.
44 based IPython widget.
45
45
46 3. Offer a flexible framework which can be used as the base
46 3. Offer a flexible framework which can be used as the base
47 environment for other systems with Python as the underlying
47 environment for other systems with Python as the underlying
48 language. Specifically scientific environments like Mathematica,
48 language. Specifically scientific environments like Mathematica,
49 IDL and Matlab inspired its design, but similar ideas can be
49 IDL and Matlab inspired its design, but similar ideas can be
50 useful in many fields.
50 useful in many fields.
51
51
52 4. Allow interactive testing of threaded graphical toolkits. IPython
52 4. Allow interactive testing of threaded graphical toolkits. IPython
53 has support for interactive, non-blocking control of GTK, Qt and
53 has support for interactive, non-blocking control of GTK, Qt and
54 WX applications via special threading flags. The normal Python
54 WX applications via special threading flags. The normal Python
55 shell can only do this for Tkinter applications.
55 shell can only do this for Tkinter applications.
56
56
57 Main features of the interactive shell
57 Main features of the interactive shell
58 --------------------------------------
58 --------------------------------------
59
59
60 * Dynamic object introspection. One can access docstrings, function
60 * Dynamic object introspection. One can access docstrings, function
61 definition prototypes, source code, source files and other details
61 definition prototypes, source code, source files and other details
62 of any object accessible to the interpreter with a single
62 of any object accessible to the interpreter with a single
63 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
63 keystroke (:samp:`?`, and using :samp:`??` provides additional detail).
64
64
65 * Searching through modules and namespaces with :samp:`*` wildcards, both
65 * Searching through modules and namespaces with :samp:`*` wildcards, both
66 when using the :samp:`?` system and via the :samp:`%psearch` command.
66 when using the :samp:`?` system and via the :samp:`%psearch` command.
67
67
68 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
68 * Completion in the local namespace, by typing :kbd:`TAB` at the prompt.
69 This works for keywords, modules, methods, variables and files in the
69 This works for keywords, modules, methods, variables and files in the
70 current directory. This is supported via the readline library, and
70 current directory. This is supported via the readline library, and
71 full access to configuring readline's behavior is provided.
71 full access to configuring readline's behavior is provided.
72 Custom completers can be implemented easily for different purposes
72 Custom completers can be implemented easily for different purposes
73 (system commands, magic arguments etc.)
73 (system commands, magic arguments etc.)
74
74
75 * Numbered input/output prompts with command history (persistent
75 * Numbered input/output prompts with command history (persistent
76 across sessions and tied to each profile), full searching in this
76 across sessions and tied to each profile), full searching in this
77 history and caching of all input and output.
77 history and caching of all input and output.
78
78
79 * User-extensible 'magic' commands. A set of commands prefixed with
79 * User-extensible 'magic' commands. A set of commands prefixed with
80 :samp:`%` is available for controlling IPython itself and provides
80 :samp:`%` is available for controlling IPython itself and provides
81 directory control, namespace information and many aliases to
81 directory control, namespace information and many aliases to
82 common system shell commands.
82 common system shell commands.
83
83
84 * Alias facility for defining your own system aliases.
84 * Alias facility for defining your own system aliases.
85
85
86 * Complete system shell access. Lines starting with :samp:`!` are passed
86 * Complete system shell access. Lines starting with :samp:`!` are passed
87 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
87 directly to the system shell, and using :samp:`!!` or :samp:`var = !cmd`
88 captures shell output into python variables for further use.
88 captures shell output into python variables for further use.
89
89
90 * Background execution of Python commands in a separate thread.
90 * Background execution of Python commands in a separate thread.
91 IPython has an internal job manager called jobs, and a
91 IPython has an internal job manager called jobs, and a
92 convenience backgrounding magic function called :samp:`%bg`.
92 convenience backgrounding magic function called :samp:`%bg`.
93
93
94 * The ability to expand python variables when calling the system
94 * The ability to expand python variables when calling the system
95 shell. In a shell command, any python variable prefixed with :samp:`$` is
95 shell. In a shell command, any python variable prefixed with :samp:`$` is
96 expanded. A double :samp:`$$` allows passing a literal :samp:`$` to the shell (for
96 expanded. A double :samp:`$$` allows passing a literal :samp:`$` to the shell (for
97 access to shell and environment variables like :envvar:`PATH`).
97 access to shell and environment variables like :envvar:`PATH`).
98
98
99 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
99 * Filesystem navigation, via a magic :samp:`%cd` command, along with a
100 persistent bookmark system (using :samp:`%bookmark`) for fast access to
100 persistent bookmark system (using :samp:`%bookmark`) for fast access to
101 frequently visited directories.
101 frequently visited directories.
102
102
103 * A lightweight persistence framework via the :samp:`%store` command, which
103 * A lightweight persistence framework via the :samp:`%store` command, which
104 allows you to save arbitrary Python variables. These get restored
104 allows you to save arbitrary Python variables. These get restored
105 automatically when your session restarts.
105 automatically when your session restarts.
106
106
107 * Automatic indentation (optional) of code as you type (through the
107 * Automatic indentation (optional) of code as you type (through the
108 readline library).
108 readline library).
109
109
110 * Macro system for quickly re-executing multiple lines of previous
110 * Macro system for quickly re-executing multiple lines of previous
111 input with a single name. Macros can be stored persistently via
111 input with a single name. Macros can be stored persistently via
112 :samp:`%store` and edited via :samp:`%edit`.
112 :samp:`%store` and edited via :samp:`%edit`.
113
113
114 * Session logging (you can then later use these logs as code in your
114 * Session logging (you can then later use these logs as code in your
115 programs). Logs can optionally timestamp all input, and also store
115 programs). Logs can optionally timestamp all input, and also store
116 session output (marked as comments, so the log remains valid
116 session output (marked as comments, so the log remains valid
117 Python source code).
117 Python source code).
118
118
119 * Session restoring: logs can be replayed to restore a previous
119 * Session restoring: logs can be replayed to restore a previous
120 session to the state where you left it.
120 session to the state where you left it.
121
121
122 * Verbose and colored exception traceback printouts. Easier to parse
122 * Verbose and colored exception traceback printouts. Easier to parse
123 visually, and in verbose mode they produce a lot of useful
123 visually, and in verbose mode they produce a lot of useful
124 debugging information (basically a terminal version of the cgitb
124 debugging information (basically a terminal version of the cgitb
125 module).
125 module).
126
126
127 * Auto-parentheses: callable objects can be executed without
127 * Auto-parentheses: callable objects can be executed without
128 parentheses: :samp:`sin 3` is automatically converted to :samp:`sin(3)`.
128 parentheses: :samp:`sin 3` is automatically converted to :samp:`sin(3)`.
129
129
130 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
130 * Auto-quoting: using :samp:`,`, or :samp:`;` as the first character forces
131 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
131 auto-quoting of the rest of the line: :samp:`,my_function a b` becomes
132 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
132 automatically :samp:`my_function("a","b")`, while :samp:`;my_function a b`
133 becomes :samp:`my_function("a b")`.
133 becomes :samp:`my_function("a b")`.
134
134
135 * Extensible input syntax. You can define filters that pre-process
135 * Extensible input syntax. You can define filters that pre-process
136 user input to simplify input in special situations. This allows
136 user input to simplify input in special situations. This allows
137 for example pasting multi-line code fragments which start with
137 for example pasting multi-line code fragments which start with
138 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
138 :samp:`>>>` or :samp:`...` such as those from other python sessions or the
139 standard Python documentation.
139 standard Python documentation.
140
140
141 * Flexible configuration system. It uses a configuration file which
141 * Flexible configuration system. It uses a configuration file which
142 allows permanent setting of all command-line options, module
142 allows permanent setting of all command-line options, module
143 loading, code and file execution. The system allows recursive file
143 loading, code and file execution. The system allows recursive file
144 inclusion, so you can have a base file with defaults and layers
144 inclusion, so you can have a base file with defaults and layers
145 which load other customizations for particular projects.
145 which load other customizations for particular projects.
146
146
147 * Embeddable. You can call IPython as a python shell inside your own
147 * Embeddable. You can call IPython as a python shell inside your own
148 python programs. This can be used both for debugging code or for
148 python programs. This can be used both for debugging code or for
149 providing interactive abilities to your programs with knowledge
149 providing interactive abilities to your programs with knowledge
150 about the local namespaces (very useful in debugging and data
150 about the local namespaces (very useful in debugging and data
151 analysis situations).
151 analysis situations).
152
152
153 * Easy debugger access. You can set IPython to call up an enhanced
153 * Easy debugger access. You can set IPython to call up an enhanced
154 version of the Python debugger (pdb) every time there is an
154 version of the Python debugger (pdb) every time there is an
155 uncaught exception. This drops you inside the code which triggered
155 uncaught exception. This drops you inside the code which triggered
156 the exception with all the data live and it is possible to
156 the exception with all the data live and it is possible to
157 navigate the stack to rapidly isolate the source of a bug. The
157 navigate the stack to rapidly isolate the source of a bug. The
158 :samp:`%run` magic command (with the :samp:`-d` option) can run any script under
158 :samp:`%run` magic command (with the :samp:`-d` option) can run any script under
159 pdb's control, automatically setting initial breakpoints for you.
159 pdb's control, automatically setting initial breakpoints for you.
160 This version of pdb has IPython-specific improvements, including
160 This version of pdb has IPython-specific improvements, including
161 tab-completion and traceback coloring support. For even easier
161 tab-completion and traceback coloring support. For even easier
162 debugger access, try :samp:`%debug` after seeing an exception. winpdb is
162 debugger access, try :samp:`%debug` after seeing an exception. winpdb is
163 also supported, see ipy_winpdb extension.
163 also supported, see ipy_winpdb extension.
164
164
165 * Profiler support. You can run single statements (similar to
165 * Profiler support. You can run single statements (similar to
166 :samp:`profile.run()`) or complete programs under the profiler's control.
166 :samp:`profile.run()`) or complete programs under the profiler's control.
167 While this is possible with standard cProfile or profile modules,
167 While this is possible with standard cProfile or profile modules,
168 IPython wraps this functionality with magic commands (see :samp:`%prun`
168 IPython wraps this functionality with magic commands (see :samp:`%prun`
169 and :samp:`%run -p`) convenient for rapid interactive work.
169 and :samp:`%run -p`) convenient for rapid interactive work.
170
170
171 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
171 * Doctest support. The special :samp:`%doctest_mode` command toggles a mode
172 that allows you to paste existing doctests (with leading :samp:`>>>`
172 that allows you to paste existing doctests (with leading :samp:`>>>`
173 prompts and whitespace) and uses doctest-compatible prompts and
173 prompts and whitespace) and uses doctest-compatible prompts and
174 output, so you can use IPython sessions as doctest code.
174 output, so you can use IPython sessions as doctest code.
175
175
176 Interactive parallel computing
176 Interactive parallel computing
177 ==============================
177 ==============================
178
178
179 Increasingly, parallel computer hardware, such as multicore CPUs, clusters and supercomputers, is becoming ubiquitous. Over the last 3 years, we have developed an
179 Increasingly, parallel computer hardware, such as multicore CPUs, clusters and supercomputers, is becoming ubiquitous. Over the last 3 years, we have developed an
180 architecture within IPython that allows such hardware to be used quickly and easily
180 architecture within IPython that allows such hardware to be used quickly and easily
181 from Python. Moreover, this architecture is designed to support interactive and
181 from Python. Moreover, this architecture is designed to support interactive and
182 collaborative parallel computing.
182 collaborative parallel computing.
183
183
184 The main features of this system are:
184 The main features of this system are:
185
185
186 * Quickly parallelize Python code from an interactive Python/IPython session.
186 * Quickly parallelize Python code from an interactive Python/IPython session.
187
187
188 * A flexible and dynamic process model that be deployed on anything from
188 * A flexible and dynamic process model that be deployed on anything from
189 multicore workstations to supercomputers.
189 multicore workstations to supercomputers.
190
190
191 * An architecture that supports many different styles of parallelism, from
191 * An architecture that supports many different styles of parallelism, from
192 message passing to task farming. And all of these styles can be handled
192 message passing to task farming. And all of these styles can be handled
193 interactively.
193 interactively.
194
194
195 * Both blocking and fully asynchronous interfaces.
195 * Both blocking and fully asynchronous interfaces.
196
196
197 * High level APIs that enable many things to be parallelized in a few lines
197 * High level APIs that enable many things to be parallelized in a few lines
198 of code.
198 of code.
199
199
200 * Write parallel code that will run unchanged on everything from multicore
200 * Write parallel code that will run unchanged on everything from multicore
201 workstations to supercomputers.
201 workstations to supercomputers.
202
202
203 * Full integration with Message Passing libraries (MPI).
203 * Full integration with Message Passing libraries (MPI).
204
204
205 * Capabilities based security model with full encryption of network connections.
205 * Capabilities based security model with full encryption of network connections.
206
206
207 * Share live parallel jobs with other users securely. We call this collaborative
207 * Share live parallel jobs with other users securely. We call this collaborative
208 parallel computing.
208 parallel computing.
209
209
210 * Dynamically load balanced task farming system.
210 * Dynamically load balanced task farming system.
211
211
212 * Robust error handling. Python exceptions raised in parallel execution are
212 * Robust error handling. Python exceptions raised in parallel execution are
213 gathered and presented to the top-level code.
213 gathered and presented to the top-level code.
214
214
215 For more information, see our :ref:`overview <parallel_index>` of using IPython for
215 For more information, see our :ref:`overview <parallel_index>` of using IPython for
216 parallel computing.
216 parallel computing.
217
217
218 Portability and Python requirements
218 Portability and Python requirements
219 -----------------------------------
219 -----------------------------------
220
220
221 As of the 0.9 release, IPython requires Python 2.4 or greater. We have
221 As of the 0.9 release, IPython requires Python 2.4 or greater. We have
222 not begun to test IPython on Python 2.6 or 3.0, but we expect it will
222 not begun to test IPython on Python 2.6 or 3.0, but we expect it will
223 work with some minor changes.
223 work with some minor changes.
224
224
225 IPython is known to work on the following operating systems:
225 IPython is known to work on the following operating systems:
226
226
227 * Linux
227 * Linux
228 * AIX
228 * AIX
229 * Most other Unix-like OSs (Solaris, BSD, etc.)
229 * Most other Unix-like OSs (Solaris, BSD, etc.)
230 * Mac OS X
230 * Mac OS X
231 * Windows (CygWin, XP, Vista, etc.)
231 * Windows (CygWin, XP, Vista, etc.)
232
232
233 See :ref:`here <install_index>` for instructions on how to install IPython. No newline at end of file
233 See :ref:`here <install_index>` for instructions on how to install IPython.
1 NO CONTENT: file renamed from IPython/config/config.py to sandbox/config.py
NO CONTENT: file renamed from IPython/config/config.py to sandbox/config.py
1 NO CONTENT: file renamed from IPython/config/tests/sample_config.py to sandbox/sample_config.py
NO CONTENT: file renamed from IPython/config/tests/sample_config.py to sandbox/sample_config.py
1 NO CONTENT: file renamed from IPython/config/tests/test_config.py to sandbox/test_config.py
NO CONTENT: file renamed from IPython/config/tests/test_config.py to sandbox/test_config.py
1 NO CONTENT: file renamed from IPython/config/traitlets.py to sandbox/traitlets.py
NO CONTENT: file renamed from IPython/config/traitlets.py to sandbox/traitlets.py
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now