Show More
The requested changes are too big and content was truncated. Show full diff
@@ -0,0 +1,256 b'' | |||||
|
1 | // !$*UTF8*$! | |||
|
2 | { | |||
|
3 | archiveVersion = 1; | |||
|
4 | classes = { | |||
|
5 | }; | |||
|
6 | objectVersion = 42; | |||
|
7 | objects = { | |||
|
8 | ||||
|
9 | /* Begin PBXContainerItemProxy section */ | |||
|
10 | 4C5B7ADB0E1A0BCD006CB905 /* PBXContainerItemProxy */ = { | |||
|
11 | isa = PBXContainerItemProxy; | |||
|
12 | containerPortal = 4C96F4FE0E199AB500B03430 /* Project object */; | |||
|
13 | proxyType = 1; | |||
|
14 | remoteGlobalIDString = 4C96F50C0E199AF100B03430; | |||
|
15 | remoteInfo = "Cocoa Frontend Plugin"; | |||
|
16 | }; | |||
|
17 | /* End PBXContainerItemProxy section */ | |||
|
18 | ||||
|
19 | /* Begin PBXFileReference section */ | |||
|
20 | 4C5B7A8D0E1A0B4C006CB905 /* Plugin-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Plugin-Info.plist"; sourceTree = "<group>"; }; | |||
|
21 | 4C5B7AD30E1A0BC8006CB905 /* Placeholder (Do Not Use).bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Placeholder (Do Not Use).bundle"; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
|
22 | 4C5B7AD40E1A0BC8006CB905 /* Placeholder (Do Not Use)-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Placeholder (Do Not Use)-Info.plist"; sourceTree = "<group>"; }; | |||
|
23 | /* End PBXFileReference section */ | |||
|
24 | ||||
|
25 | /* Begin PBXFrameworksBuildPhase section */ | |||
|
26 | 4C5B7AD10E1A0BC8006CB905 /* Frameworks */ = { | |||
|
27 | isa = PBXFrameworksBuildPhase; | |||
|
28 | buildActionMask = 2147483647; | |||
|
29 | files = ( | |||
|
30 | ); | |||
|
31 | runOnlyForDeploymentPostprocessing = 0; | |||
|
32 | }; | |||
|
33 | /* End PBXFrameworksBuildPhase section */ | |||
|
34 | ||||
|
35 | /* Begin PBXGroup section */ | |||
|
36 | 4C5B7A8C0E1A0B4C006CB905 /* Products */ = { | |||
|
37 | isa = PBXGroup; | |||
|
38 | children = ( | |||
|
39 | 4C5B7AD30E1A0BC8006CB905 /* Placeholder (Do Not Use).bundle */, | |||
|
40 | ); | |||
|
41 | name = Products; | |||
|
42 | sourceTree = "<group>"; | |||
|
43 | }; | |||
|
44 | 4C96F4FC0E199AB500B03430 = { | |||
|
45 | isa = PBXGroup; | |||
|
46 | children = ( | |||
|
47 | 4C5B7A8C0E1A0B4C006CB905 /* Products */, | |||
|
48 | 4C5B7A8D0E1A0B4C006CB905 /* Plugin-Info.plist */, | |||
|
49 | 4C5B7AD40E1A0BC8006CB905 /* Placeholder (Do Not Use)-Info.plist */, | |||
|
50 | ); | |||
|
51 | sourceTree = "<group>"; | |||
|
52 | }; | |||
|
53 | /* End PBXGroup section */ | |||
|
54 | ||||
|
55 | /* Begin PBXLegacyTarget section */ | |||
|
56 | 4C96F50C0E199AF100B03430 /* Cocoa Frontend Plugin */ = { | |||
|
57 | isa = PBXLegacyTarget; | |||
|
58 | buildArgumentsString = "$(ACTION)"; | |||
|
59 | buildConfigurationList = 4C96F5110E199B3300B03430 /* Build configuration list for PBXLegacyTarget "Cocoa Frontend Plugin" */; | |||
|
60 | buildPhases = ( | |||
|
61 | ); | |||
|
62 | buildToolPath = /usr/bin/make; | |||
|
63 | buildWorkingDirectory = ""; | |||
|
64 | dependencies = ( | |||
|
65 | ); | |||
|
66 | name = "Cocoa Frontend Plugin"; | |||
|
67 | passBuildSettingsInEnvironment = 1; | |||
|
68 | productName = "Cocoa Frontend Plugin"; | |||
|
69 | }; | |||
|
70 | /* End PBXLegacyTarget section */ | |||
|
71 | ||||
|
72 | /* Begin PBXNativeTarget section */ | |||
|
73 | 4C5B7AD20E1A0BC8006CB905 /* Placeholder (Do Not Use) */ = { | |||
|
74 | isa = PBXNativeTarget; | |||
|
75 | buildConfigurationList = 4C5B7ADA0E1A0BC9006CB905 /* Build configuration list for PBXNativeTarget "Placeholder (Do Not Use)" */; | |||
|
76 | buildPhases = ( | |||
|
77 | 4C5B7ACF0E1A0BC8006CB905 /* Resources */, | |||
|
78 | 4C5B7AD00E1A0BC8006CB905 /* Sources */, | |||
|
79 | 4C5B7AD10E1A0BC8006CB905 /* Frameworks */, | |||
|
80 | ); | |||
|
81 | buildRules = ( | |||
|
82 | ); | |||
|
83 | dependencies = ( | |||
|
84 | 4C5B7ADC0E1A0BCD006CB905 /* PBXTargetDependency */, | |||
|
85 | ); | |||
|
86 | name = "Placeholder (Do Not Use)"; | |||
|
87 | productName = "Placeholder (Do Not Use)"; | |||
|
88 | productReference = 4C5B7AD30E1A0BC8006CB905 /* Placeholder (Do Not Use).bundle */; | |||
|
89 | productType = "com.apple.product-type.bundle"; | |||
|
90 | }; | |||
|
91 | /* End PBXNativeTarget section */ | |||
|
92 | ||||
|
93 | /* Begin PBXProject section */ | |||
|
94 | 4C96F4FE0E199AB500B03430 /* Project object */ = { | |||
|
95 | isa = PBXProject; | |||
|
96 | buildConfigurationList = 4C96F5010E199AB500B03430 /* Build configuration list for PBXProject "CocoaFrontendPlugin" */; | |||
|
97 | compatibilityVersion = "Xcode 2.4"; | |||
|
98 | hasScannedForEncodings = 0; | |||
|
99 | mainGroup = 4C96F4FC0E199AB500B03430; | |||
|
100 | productRefGroup = 4C5B7A8C0E1A0B4C006CB905 /* Products */; | |||
|
101 | projectDirPath = ""; | |||
|
102 | projectRoot = ""; | |||
|
103 | targets = ( | |||
|
104 | 4C96F50C0E199AF100B03430 /* Cocoa Frontend Plugin */, | |||
|
105 | 4C5B7AD20E1A0BC8006CB905 /* Placeholder (Do Not Use) */, | |||
|
106 | ); | |||
|
107 | }; | |||
|
108 | /* End PBXProject section */ | |||
|
109 | ||||
|
110 | /* Begin PBXResourcesBuildPhase section */ | |||
|
111 | 4C5B7ACF0E1A0BC8006CB905 /* Resources */ = { | |||
|
112 | isa = PBXResourcesBuildPhase; | |||
|
113 | buildActionMask = 2147483647; | |||
|
114 | files = ( | |||
|
115 | ); | |||
|
116 | runOnlyForDeploymentPostprocessing = 0; | |||
|
117 | }; | |||
|
118 | /* End PBXResourcesBuildPhase section */ | |||
|
119 | ||||
|
120 | /* Begin PBXSourcesBuildPhase section */ | |||
|
121 | 4C5B7AD00E1A0BC8006CB905 /* Sources */ = { | |||
|
122 | isa = PBXSourcesBuildPhase; | |||
|
123 | buildActionMask = 2147483647; | |||
|
124 | files = ( | |||
|
125 | ); | |||
|
126 | runOnlyForDeploymentPostprocessing = 0; | |||
|
127 | }; | |||
|
128 | /* End PBXSourcesBuildPhase section */ | |||
|
129 | ||||
|
130 | /* Begin PBXTargetDependency section */ | |||
|
131 | 4C5B7ADC0E1A0BCD006CB905 /* PBXTargetDependency */ = { | |||
|
132 | isa = PBXTargetDependency; | |||
|
133 | target = 4C96F50C0E199AF100B03430 /* Cocoa Frontend Plugin */; | |||
|
134 | targetProxy = 4C5B7ADB0E1A0BCD006CB905 /* PBXContainerItemProxy */; | |||
|
135 | }; | |||
|
136 | /* End PBXTargetDependency section */ | |||
|
137 | ||||
|
138 | /* Begin XCBuildConfiguration section */ | |||
|
139 | 4C5B7AD50E1A0BC9006CB905 /* Debug */ = { | |||
|
140 | isa = XCBuildConfiguration; | |||
|
141 | buildSettings = { | |||
|
142 | COPY_PHASE_STRIP = NO; | |||
|
143 | GCC_DYNAMIC_NO_PIC = NO; | |||
|
144 | GCC_ENABLE_FIX_AND_CONTINUE = YES; | |||
|
145 | GCC_MODEL_TUNING = G5; | |||
|
146 | GCC_OPTIMIZATION_LEVEL = 0; | |||
|
147 | GCC_PRECOMPILE_PREFIX_HEADER = YES; | |||
|
148 | GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; | |||
|
149 | INFOPLIST_FILE = "Placeholder (Do Not Use)-Info.plist"; | |||
|
150 | INSTALL_PATH = "$(HOME)/Library/Bundles"; | |||
|
151 | OTHER_LDFLAGS = ( | |||
|
152 | "-framework", | |||
|
153 | Foundation, | |||
|
154 | "-framework", | |||
|
155 | AppKit, | |||
|
156 | ); | |||
|
157 | PREBINDING = NO; | |||
|
158 | PRODUCT_NAME = "Placeholder (Do Not Use)"; | |||
|
159 | WRAPPER_EXTENSION = bundle; | |||
|
160 | ZERO_LINK = YES; | |||
|
161 | }; | |||
|
162 | name = Debug; | |||
|
163 | }; | |||
|
164 | 4C5B7AD60E1A0BC9006CB905 /* Release */ = { | |||
|
165 | isa = XCBuildConfiguration; | |||
|
166 | buildSettings = { | |||
|
167 | COPY_PHASE_STRIP = YES; | |||
|
168 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | |||
|
169 | GCC_ENABLE_FIX_AND_CONTINUE = NO; | |||
|
170 | GCC_MODEL_TUNING = G5; | |||
|
171 | GCC_PRECOMPILE_PREFIX_HEADER = YES; | |||
|
172 | GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; | |||
|
173 | INFOPLIST_FILE = "Placeholder (Do Not Use)-Info.plist"; | |||
|
174 | INSTALL_PATH = "$(HOME)/Library/Bundles"; | |||
|
175 | OTHER_LDFLAGS = ( | |||
|
176 | "-framework", | |||
|
177 | Foundation, | |||
|
178 | "-framework", | |||
|
179 | AppKit, | |||
|
180 | ); | |||
|
181 | PREBINDING = NO; | |||
|
182 | PRODUCT_NAME = "Placeholder (Do Not Use)"; | |||
|
183 | WRAPPER_EXTENSION = bundle; | |||
|
184 | ZERO_LINK = NO; | |||
|
185 | }; | |||
|
186 | name = Release; | |||
|
187 | }; | |||
|
188 | 4C96F4FF0E199AB500B03430 /* Debug */ = { | |||
|
189 | isa = XCBuildConfiguration; | |||
|
190 | buildSettings = { | |||
|
191 | COPY_PHASE_STRIP = NO; | |||
|
192 | }; | |||
|
193 | name = Debug; | |||
|
194 | }; | |||
|
195 | 4C96F5000E199AB500B03430 /* Release */ = { | |||
|
196 | isa = XCBuildConfiguration; | |||
|
197 | buildSettings = { | |||
|
198 | COPY_PHASE_STRIP = YES; | |||
|
199 | }; | |||
|
200 | name = Release; | |||
|
201 | }; | |||
|
202 | 4C96F50D0E199AF100B03430 /* Debug */ = { | |||
|
203 | isa = XCBuildConfiguration; | |||
|
204 | buildSettings = { | |||
|
205 | COPY_PHASE_STRIP = NO; | |||
|
206 | GCC_DYNAMIC_NO_PIC = NO; | |||
|
207 | GCC_OPTIMIZATION_LEVEL = 0; | |||
|
208 | PRODUCT_NAME = "Cocoa Frontend Plugin"; | |||
|
209 | }; | |||
|
210 | name = Debug; | |||
|
211 | }; | |||
|
212 | 4C96F50E0E199AF100B03430 /* Release */ = { | |||
|
213 | isa = XCBuildConfiguration; | |||
|
214 | buildSettings = { | |||
|
215 | COPY_PHASE_STRIP = YES; | |||
|
216 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | |||
|
217 | GCC_ENABLE_FIX_AND_CONTINUE = NO; | |||
|
218 | PRODUCT_NAME = "Cocoa Frontend Plugin"; | |||
|
219 | ZERO_LINK = NO; | |||
|
220 | }; | |||
|
221 | name = Release; | |||
|
222 | }; | |||
|
223 | /* End XCBuildConfiguration section */ | |||
|
224 | ||||
|
225 | /* Begin XCConfigurationList section */ | |||
|
226 | 4C5B7ADA0E1A0BC9006CB905 /* Build configuration list for PBXNativeTarget "Placeholder (Do Not Use)" */ = { | |||
|
227 | isa = XCConfigurationList; | |||
|
228 | buildConfigurations = ( | |||
|
229 | 4C5B7AD50E1A0BC9006CB905 /* Debug */, | |||
|
230 | 4C5B7AD60E1A0BC9006CB905 /* Release */, | |||
|
231 | ); | |||
|
232 | defaultConfigurationIsVisible = 0; | |||
|
233 | defaultConfigurationName = Release; | |||
|
234 | }; | |||
|
235 | 4C96F5010E199AB500B03430 /* Build configuration list for PBXProject "CocoaFrontendPlugin" */ = { | |||
|
236 | isa = XCConfigurationList; | |||
|
237 | buildConfigurations = ( | |||
|
238 | 4C96F4FF0E199AB500B03430 /* Debug */, | |||
|
239 | 4C96F5000E199AB500B03430 /* Release */, | |||
|
240 | ); | |||
|
241 | defaultConfigurationIsVisible = 0; | |||
|
242 | defaultConfigurationName = Release; | |||
|
243 | }; | |||
|
244 | 4C96F5110E199B3300B03430 /* Build configuration list for PBXLegacyTarget "Cocoa Frontend Plugin" */ = { | |||
|
245 | isa = XCConfigurationList; | |||
|
246 | buildConfigurations = ( | |||
|
247 | 4C96F50D0E199AF100B03430 /* Debug */, | |||
|
248 | 4C96F50E0E199AF100B03430 /* Release */, | |||
|
249 | ); | |||
|
250 | defaultConfigurationIsVisible = 0; | |||
|
251 | defaultConfigurationName = Release; | |||
|
252 | }; | |||
|
253 | /* End XCConfigurationList section */ | |||
|
254 | }; | |||
|
255 | rootObject = 4C96F4FE0E199AB500B03430 /* Project object */; | |||
|
256 | } |
@@ -0,0 +1,25 b'' | |||||
|
1 | # encoding: utf-8 | |||
|
2 | """ | |||
|
3 | Provides a namespace for loading the Cocoa frontend via a Cocoa plugin. | |||
|
4 | ||||
|
5 | Author: Barry Wark | |||
|
6 | """ | |||
|
7 | __docformat__ = "restructuredtext en" | |||
|
8 | ||||
|
9 | #----------------------------------------------------------------------------- | |||
|
10 | # Copyright (C) 2008 The IPython Development Team | |||
|
11 | # | |||
|
12 | # Distributed under the terms of the BSD License. The full license is in | |||
|
13 | # the file COPYING, distributed as part of this software. | |||
|
14 | #----------------------------------------------------------------------------- | |||
|
15 | ||||
|
16 | from PyObjCTools import AppHelper | |||
|
17 | from twisted.internet import _threadedselect | |||
|
18 | ||||
|
19 | #make sure _threadedselect is installed first | |||
|
20 | reactor = _threadedselect.install() | |||
|
21 | ||||
|
22 | # load the Cocoa frontend controller | |||
|
23 | from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController | |||
|
24 | reactor.interleave(AppHelper.callAfter) | |||
|
25 | assert(reactor.running) |
@@ -0,0 +1,6 b'' | |||||
|
1 | include ./plugins.mk | |||
|
2 | ||||
|
3 | all : dist/IPythonCocoaController.plugin | |||
|
4 | ||||
|
5 | dist/IPythonCocoaController.plugin : ./IPythonCocoaFrontendLoader.py\ | |||
|
6 | ./setup.py No newline at end of file |
@@ -0,0 +1,20 b'' | |||||
|
1 | <?xml version="1.0" encoding="UTF-8"?> | |||
|
2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||
|
3 | <plist version="1.0"> | |||
|
4 | <dict> | |||
|
5 | <key>CFBundleDevelopmentRegion</key> | |||
|
6 | <string>English</string> | |||
|
7 | <key>CFBundleExecutable</key> | |||
|
8 | <string>${EXECUTABLE_NAME}</string> | |||
|
9 | <key>CFBundleIdentifier</key> | |||
|
10 | <string>com.yourcompany.Placeholder (Do Not Use)</string> | |||
|
11 | <key>CFBundleInfoDictionaryVersion</key> | |||
|
12 | <string>6.0</string> | |||
|
13 | <key>CFBundlePackageType</key> | |||
|
14 | <string>BNDL</string> | |||
|
15 | <key>CFBundleSignature</key> | |||
|
16 | <string>????</string> | |||
|
17 | <key>CFBundleVersion</key> | |||
|
18 | <string>1.0</string> | |||
|
19 | </dict> | |||
|
20 | </plist> |
@@ -0,0 +1,21 b'' | |||||
|
1 | %.plugin:: | |||
|
2 | rm -rf dist/$(notdir $@) | |||
|
3 | rm -rf build dist && \ | |||
|
4 | python setup.py py2app -s | |||
|
5 | ||||
|
6 | %.py: | |||
|
7 | @echo "test -f $@" | |||
|
8 | @test -f %@ | |||
|
9 | ||||
|
10 | %.nib: | |||
|
11 | @echo "test -f $@" | |||
|
12 | @test -f %@ | |||
|
13 | ||||
|
14 | .DEFAULT_GOAL := all | |||
|
15 | ||||
|
16 | .PHONY : all clean | |||
|
17 | ||||
|
18 | clean : | |||
|
19 | rm -rf build dist | |||
|
20 | ||||
|
21 |
@@ -0,0 +1,35 b'' | |||||
|
1 | # encoding: utf-8 | |||
|
2 | """ | |||
|
3 | setup.py | |||
|
4 | ||||
|
5 | Setuptools installer script for generating a Cocoa plugin for the | |||
|
6 | IPython cocoa frontend | |||
|
7 | ||||
|
8 | Author: Barry Wark | |||
|
9 | """ | |||
|
10 | __docformat__ = "restructuredtext en" | |||
|
11 | ||||
|
12 | #----------------------------------------------------------------------------- | |||
|
13 | # Copyright (C) 2008 The IPython Development Team | |||
|
14 | # | |||
|
15 | # Distributed under the terms of the BSD License. The full license is in | |||
|
16 | # the file COPYING, distributed as part of this software. | |||
|
17 | #----------------------------------------------------------------------------- | |||
|
18 | ||||
|
19 | from setuptools import setup | |||
|
20 | ||||
|
21 | infoPlist = dict( | |||
|
22 | CFBundleDevelopmentRegion='English', | |||
|
23 | CFBundleIdentifier='org.scipy.ipython.cocoa_frontend', | |||
|
24 | NSPrincipalClass='IPythonCocoaController', | |||
|
25 | ) | |||
|
26 | ||||
|
27 | setup( | |||
|
28 | plugin=['IPythonCocoaFrontendLoader.py'], | |||
|
29 | setup_requires=['py2app'], | |||
|
30 | options=dict(py2app=dict( | |||
|
31 | plist=infoPlist, | |||
|
32 | site_packages=True, | |||
|
33 | excludes=['IPython','twisted','PyObjCTools'] | |||
|
34 | )), | |||
|
35 | ) No newline at end of file |
@@ -0,0 +1,20 b'' | |||||
|
1 | # Set this prefix to where you want to install the plugin | |||
|
2 | PREFIX=~/usr/local | |||
|
3 | PREFIX=~/tmp/local | |||
|
4 | ||||
|
5 | plugin: IPython_doctest_plugin.egg-info | |||
|
6 | ||||
|
7 | test: plugin dtexample.py | |||
|
8 | nosetests -s --with-ipdoctest --doctest-tests --doctest-extension=txt \ | |||
|
9 | dtexample.py test*.txt | |||
|
10 | ||||
|
11 | deb: plugin dtexample.py | |||
|
12 | nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \ | |||
|
13 | test_combo.txt | |||
|
14 | ||||
|
15 | IPython_doctest_plugin.egg-info: ipdoctest.py setup.py | |||
|
16 | python setup.py install --prefix=$(PREFIX) | |||
|
17 | touch $@ | |||
|
18 | ||||
|
19 | clean: | |||
|
20 | rm -rf IPython_doctest_plugin.egg-info *~ *pyc build/ dist/ |
@@ -0,0 +1,39 b'' | |||||
|
1 | ======================================================= | |||
|
2 | Nose plugin with IPython and extension module support | |||
|
3 | ======================================================= | |||
|
4 | ||||
|
5 | This directory provides the key functionality for test support that IPython | |||
|
6 | needs as a nose plugin, which can be installed for use in projects other than | |||
|
7 | IPython. | |||
|
8 | ||||
|
9 | The presence of a Makefile here is mostly for development and debugging | |||
|
10 | purposes as it only provides a few shorthand commands. You can manually | |||
|
11 | install the plugin by using standard Python procedures (``setup.py install`` | |||
|
12 | with appropriate arguments). | |||
|
13 | ||||
|
14 | To install the plugin using the Makefile, edit its first line to reflect where | |||
|
15 | you'd like the installation. If you want it system-wide, you may want to edit | |||
|
16 | the install line in the plugin target to use sudo and no prefix:: | |||
|
17 | ||||
|
18 | sudo python setup.py install | |||
|
19 | ||||
|
20 | instead of the code using `--prefix` that's in there. | |||
|
21 | ||||
|
22 | Once you've set the prefix, simply build/install the plugin with:: | |||
|
23 | ||||
|
24 | make | |||
|
25 | ||||
|
26 | and run the tests with:: | |||
|
27 | ||||
|
28 | make test | |||
|
29 | ||||
|
30 | You should see output similar to:: | |||
|
31 | ||||
|
32 | maqroll[plugin]> make test | |||
|
33 | nosetests -s --with-ipdoctest --doctest-tests dtexample.py | |||
|
34 | .. | |||
|
35 | ---------------------------------------------------------------------- | |||
|
36 | Ran 2 tests in 0.016s | |||
|
37 | ||||
|
38 | OK | |||
|
39 |
@@ -0,0 +1,72 b'' | |||||
|
1 | """Simple example using doctests. | |||
|
2 | ||||
|
3 | This file just contains doctests both using plain python and IPython prompts. | |||
|
4 | All tests should be loaded by nose. | |||
|
5 | """ | |||
|
6 | ||||
|
7 | def pyfunc(): | |||
|
8 | """Some pure python tests... | |||
|
9 | ||||
|
10 | >>> pyfunc() | |||
|
11 | 'pyfunc' | |||
|
12 | ||||
|
13 | >>> import os | |||
|
14 | ||||
|
15 | >>> 2+3 | |||
|
16 | 5 | |||
|
17 | ||||
|
18 | >>> for i in range(3): | |||
|
19 | ... print i, | |||
|
20 | ... print i+1, | |||
|
21 | ... | |||
|
22 | 0 1 1 2 2 3 | |||
|
23 | """ | |||
|
24 | ||||
|
25 | return 'pyfunc' | |||
|
26 | ||||
|
27 | def ipfunc(): | |||
|
28 | """Some ipython tests... | |||
|
29 | ||||
|
30 | In [1]: import os | |||
|
31 | ||||
|
32 | In [2]: cd / | |||
|
33 | / | |||
|
34 | ||||
|
35 | In [3]: 2+3 | |||
|
36 | Out[3]: 5 | |||
|
37 | ||||
|
38 | In [26]: for i in range(3): | |||
|
39 | ....: print i, | |||
|
40 | ....: print i+1, | |||
|
41 | ....: | |||
|
42 | 0 1 1 2 2 3 | |||
|
43 | ||||
|
44 | ||||
|
45 | Examples that access the operating system work: | |||
|
46 | ||||
|
47 | In [1]: !echo hello | |||
|
48 | hello | |||
|
49 | ||||
|
50 | In [2]: !echo hello > /tmp/foo | |||
|
51 | ||||
|
52 | In [3]: !cat /tmp/foo | |||
|
53 | hello | |||
|
54 | ||||
|
55 | In [4]: rm -f /tmp/foo | |||
|
56 | ||||
|
57 | It's OK to use '_' for the last result, but do NOT try to use IPython's | |||
|
58 | numbered history of _NN outputs, since those won't exist under the | |||
|
59 | doctest environment: | |||
|
60 | ||||
|
61 | In [7]: 3+4 | |||
|
62 | Out[7]: 7 | |||
|
63 | ||||
|
64 | In [8]: _+3 | |||
|
65 | Out[8]: 10 | |||
|
66 | ||||
|
67 | In [9]: ipfunc() | |||
|
68 | Out[9]: 'ipfunc' | |||
|
69 | """ | |||
|
70 | ||||
|
71 | return 'ipfunc' | |||
|
72 |
This diff has been collapsed as it changes many lines, (587 lines changed) Show them Hide them | |||||
@@ -0,0 +1,587 b'' | |||||
|
1 | """Nose Plugin that supports IPython doctests. | |||
|
2 | ||||
|
3 | Limitations: | |||
|
4 | ||||
|
5 | - When generating examples for use as doctests, make sure that you have | |||
|
6 | pretty-printing OFF. This can be done either by starting ipython with the | |||
|
7 | flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by | |||
|
8 | interactively disabling it with %Pprint. This is required so that IPython | |||
|
9 | output matches that of normal Python, which is used by doctest for internal | |||
|
10 | execution. | |||
|
11 | ||||
|
12 | - Do not rely on specific prompt numbers for results (such as using | |||
|
13 | '_34==True', for example). For IPython tests run via an external process the | |||
|
14 | prompt numbers may be different, and IPython tests run as normal python code | |||
|
15 | won't even have these special _NN variables set at all. | |||
|
16 | ||||
|
17 | - IPython functions that produce output as a side-effect of calling a system | |||
|
18 | process (e.g. 'ls') can be doc-tested, but they must be handled in an | |||
|
19 | external IPython process. Such doctests must be tagged with: | |||
|
20 | ||||
|
21 | # ipdoctest: EXTERNAL | |||
|
22 | ||||
|
23 | so that the testing machinery handles them differently. Since these are run | |||
|
24 | via pexpect in an external process, they can't deal with exceptions or other | |||
|
25 | fancy featurs of regular doctests. You must limit such tests to simple | |||
|
26 | matching of the output. For this reason, I recommend you limit these kinds | |||
|
27 | of doctests to features that truly require a separate process, and use the | |||
|
28 | normal IPython ones (which have all the features of normal doctests) for | |||
|
29 | everything else. See the examples at the bottom of this file for a | |||
|
30 | comparison of what can be done with both types. | |||
|
31 | """ | |||
|
32 | ||||
|
33 | ||||
|
34 | #----------------------------------------------------------------------------- | |||
|
35 | # Module imports | |||
|
36 | ||||
|
37 | # From the standard library | |||
|
38 | import __builtin__ | |||
|
39 | import commands | |||
|
40 | import doctest | |||
|
41 | import inspect | |||
|
42 | import logging | |||
|
43 | import os | |||
|
44 | import re | |||
|
45 | import sys | |||
|
46 | import unittest | |||
|
47 | ||||
|
48 | from inspect import getmodule | |||
|
49 | ||||
|
50 | # Third-party modules | |||
|
51 | import nose.core | |||
|
52 | ||||
|
53 | from nose.plugins import doctests, Plugin | |||
|
54 | from nose.util import anyp, getpackage, test_address, resolve_name, tolist | |||
|
55 | ||||
|
56 | # Our own imports | |||
|
57 | #from extdoctest import ExtensionDoctest, DocTestFinder | |||
|
58 | #from dttools import DocTestFinder, DocTestCase | |||
|
59 | #----------------------------------------------------------------------------- | |||
|
60 | # Module globals and other constants | |||
|
61 | ||||
|
62 | log = logging.getLogger(__name__) | |||
|
63 | ||||
|
64 | ########################################################################### | |||
|
65 | # *** HACK *** | |||
|
66 | # We must start our own ipython object and heavily muck with it so that all the | |||
|
67 | # modifications IPython makes to system behavior don't send the doctest | |||
|
68 | # machinery into a fit. This code should be considered a gross hack, but it | |||
|
69 | # gets the job done. | |||
|
70 | ||||
|
71 | def start_ipython(): | |||
|
72 | """Start a global IPython shell, which we need for IPython-specific syntax. | |||
|
73 | """ | |||
|
74 | import IPython | |||
|
75 | ||||
|
76 | def xsys(cmd): | |||
|
77 | """Execute a command and print its output. | |||
|
78 | ||||
|
79 | This is just a convenience function to replace the IPython system call | |||
|
80 | with one that is more doctest-friendly. | |||
|
81 | """ | |||
|
82 | cmd = _ip.IP.var_expand(cmd,depth=1) | |||
|
83 | sys.stdout.write(commands.getoutput(cmd)) | |||
|
84 | sys.stdout.flush() | |||
|
85 | ||||
|
86 | # Store certain global objects that IPython modifies | |||
|
87 | _displayhook = sys.displayhook | |||
|
88 | _excepthook = sys.excepthook | |||
|
89 | _main = sys.modules.get('__main__') | |||
|
90 | ||||
|
91 | # Start IPython instance | |||
|
92 | IPython.Shell.IPShell(['--classic','--noterm_title']) | |||
|
93 | ||||
|
94 | # Deactivate the various python system hooks added by ipython for | |||
|
95 | # interactive convenience so we don't confuse the doctest system | |||
|
96 | sys.modules['__main__'] = _main | |||
|
97 | sys.displayhook = _displayhook | |||
|
98 | sys.excepthook = _excepthook | |||
|
99 | ||||
|
100 | # So that ipython magics and aliases can be doctested (they work by making | |||
|
101 | # a call into a global _ip object) | |||
|
102 | _ip = IPython.ipapi.get() | |||
|
103 | __builtin__._ip = _ip | |||
|
104 | ||||
|
105 | # Modify the IPython system call with one that uses getoutput, so that we | |||
|
106 | # can capture subcommands and print them to Python's stdout, otherwise the | |||
|
107 | # doctest machinery would miss them. | |||
|
108 | _ip.system = xsys | |||
|
109 | ||||
|
110 | # The start call MUST be made here. I'm not sure yet why it doesn't work if | |||
|
111 | # it is made later, at plugin initialization time, but in all my tests, that's | |||
|
112 | # the case. | |||
|
113 | start_ipython() | |||
|
114 | ||||
|
115 | # *** END HACK *** | |||
|
116 | ########################################################################### | |||
|
117 | ||||
|
118 | #----------------------------------------------------------------------------- | |||
|
119 | # Modified version of the one in the stdlib, that fixes a python bug (doctests | |||
|
120 | # not found in extension modules, http://bugs.python.org/issue3158) | |||
|
121 | class DocTestFinder(doctest.DocTestFinder): | |||
|
122 | ||||
|
123 | def _from_module(self, module, object): | |||
|
124 | """ | |||
|
125 | Return true if the given object is defined in the given | |||
|
126 | module. | |||
|
127 | """ | |||
|
128 | if module is None: | |||
|
129 | #print '_fm C1' # dbg | |||
|
130 | return True | |||
|
131 | elif inspect.isfunction(object): | |||
|
132 | #print '_fm C2' # dbg | |||
|
133 | return module.__dict__ is object.func_globals | |||
|
134 | elif inspect.isbuiltin(object): | |||
|
135 | #print '_fm C2-1' # dbg | |||
|
136 | return module.__name__ == object.__module__ | |||
|
137 | elif inspect.isclass(object): | |||
|
138 | #print '_fm C3' # dbg | |||
|
139 | return module.__name__ == object.__module__ | |||
|
140 | elif inspect.ismethod(object): | |||
|
141 | # This one may be a bug in cython that fails to correctly set the | |||
|
142 | # __module__ attribute of methods, but since the same error is easy | |||
|
143 | # to make by extension code writers, having this safety in place | |||
|
144 | # isn't such a bad idea | |||
|
145 | #print '_fm C3-1' # dbg | |||
|
146 | return module.__name__ == object.im_class.__module__ | |||
|
147 | elif inspect.getmodule(object) is not None: | |||
|
148 | #print '_fm C4' # dbg | |||
|
149 | #print 'C4 mod',module,'obj',object # dbg | |||
|
150 | return module is inspect.getmodule(object) | |||
|
151 | elif hasattr(object, '__module__'): | |||
|
152 | #print '_fm C5' # dbg | |||
|
153 | return module.__name__ == object.__module__ | |||
|
154 | elif isinstance(object, property): | |||
|
155 | #print '_fm C6' # dbg | |||
|
156 | return True # [XX] no way not be sure. | |||
|
157 | else: | |||
|
158 | raise ValueError("object must be a class or function") | |||
|
159 | ||||
|
160 | ||||
|
161 | ||||
|
162 | def _find(self, tests, obj, name, module, source_lines, globs, seen): | |||
|
163 | """ | |||
|
164 | Find tests for the given object and any contained objects, and | |||
|
165 | add them to `tests`. | |||
|
166 | """ | |||
|
167 | ||||
|
168 | doctest.DocTestFinder._find(self,tests, obj, name, module, | |||
|
169 | source_lines, globs, seen) | |||
|
170 | ||||
|
171 | # Below we re-run pieces of the above method with manual modifications, | |||
|
172 | # because the original code is buggy and fails to correctly identify | |||
|
173 | # doctests in extension modules. | |||
|
174 | ||||
|
175 | # Local shorthands | |||
|
176 | from inspect import isroutine, isclass, ismodule | |||
|
177 | ||||
|
178 | # Look for tests in a module's contained objects. | |||
|
179 | if inspect.ismodule(obj) and self._recurse: | |||
|
180 | for valname, val in obj.__dict__.items(): | |||
|
181 | valname1 = '%s.%s' % (name, valname) | |||
|
182 | if ( (isroutine(val) or isclass(val)) | |||
|
183 | and self._from_module(module, val) ): | |||
|
184 | ||||
|
185 | self._find(tests, val, valname1, module, source_lines, | |||
|
186 | globs, seen) | |||
|
187 | ||||
|
188 | ||||
|
189 | # Look for tests in a class's contained objects. | |||
|
190 | if inspect.isclass(obj) and self._recurse: | |||
|
191 | #print 'RECURSE into class:',obj # dbg | |||
|
192 | for valname, val in obj.__dict__.items(): | |||
|
193 | #valname1 = '%s.%s' % (name, valname) # dbg | |||
|
194 | #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg | |||
|
195 | # Special handling for staticmethod/classmethod. | |||
|
196 | if isinstance(val, staticmethod): | |||
|
197 | val = getattr(obj, valname) | |||
|
198 | if isinstance(val, classmethod): | |||
|
199 | val = getattr(obj, valname).im_func | |||
|
200 | ||||
|
201 | # Recurse to methods, properties, and nested classes. | |||
|
202 | if ((inspect.isfunction(val) or inspect.isclass(val) or | |||
|
203 | inspect.ismethod(val) or | |||
|
204 | isinstance(val, property)) and | |||
|
205 | self._from_module(module, val)): | |||
|
206 | valname = '%s.%s' % (name, valname) | |||
|
207 | self._find(tests, val, valname, module, source_lines, | |||
|
208 | globs, seen) | |||
|
209 | ||||
|
210 | ||||
|
211 | class DocTestCase(doctests.DocTestCase): | |||
|
212 | """Proxy for DocTestCase: provides an address() method that | |||
|
213 | returns the correct address for the doctest case. Otherwise | |||
|
214 | acts as a proxy to the test case. To provide hints for address(), | |||
|
215 | an obj may also be passed -- this will be used as the test object | |||
|
216 | for purposes of determining the test address, if it is provided. | |||
|
217 | """ | |||
|
218 | ||||
|
219 | # doctests loaded via find(obj) omit the module name | |||
|
220 | # so we need to override id, __repr__ and shortDescription | |||
|
221 | # bonus: this will squash a 2.3 vs 2.4 incompatiblity | |||
|
222 | def id(self): | |||
|
223 | name = self._dt_test.name | |||
|
224 | filename = self._dt_test.filename | |||
|
225 | if filename is not None: | |||
|
226 | pk = getpackage(filename) | |||
|
227 | if pk is not None and not name.startswith(pk): | |||
|
228 | name = "%s.%s" % (pk, name) | |||
|
229 | return name | |||
|
230 | ||||
|
231 | ||||
|
232 | # Classes and functions | |||
|
233 | ||||
|
234 | def is_extension_module(filename): | |||
|
235 | """Return whether the given filename is an extension module. | |||
|
236 | ||||
|
237 | This simply checks that the extension is either .so or .pyd. | |||
|
238 | """ | |||
|
239 | return os.path.splitext(filename)[1].lower() in ('.so','.pyd') | |||
|
240 | ||||
|
241 | ||||
|
242 | # A simple subclassing of the original with a different class name, so we can | |||
|
243 | # distinguish and treat differently IPython examples from pure python ones. | |||
|
244 | class IPExample(doctest.Example): pass | |||
|
245 | ||||
|
246 | class IPExternalExample(doctest.Example): | |||
|
247 | """Doctest examples to be run in an external process.""" | |||
|
248 | ||||
|
249 | def __init__(self, source, want, exc_msg=None, lineno=0, indent=0, | |||
|
250 | options=None): | |||
|
251 | # Parent constructor | |||
|
252 | doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options) | |||
|
253 | ||||
|
254 | # An EXTRA newline is needed to prevent pexpect hangs | |||
|
255 | self.source += '\n' | |||
|
256 | ||||
|
257 | class IPDocTestParser(doctest.DocTestParser): | |||
|
258 | """ | |||
|
259 | A class used to parse strings containing doctest examples. | |||
|
260 | ||||
|
261 | Note: This is a version modified to properly recognize IPython input and | |||
|
262 | convert any IPython examples into valid Python ones. | |||
|
263 | """ | |||
|
264 | # This regular expression is used to find doctest examples in a | |||
|
265 | # string. It defines three groups: `source` is the source code | |||
|
266 | # (including leading indentation and prompts); `indent` is the | |||
|
267 | # indentation of the first (PS1) line of the source code; and | |||
|
268 | # `want` is the expected output (including leading indentation). | |||
|
269 | ||||
|
270 | # Classic Python prompts or default IPython ones | |||
|
271 | _PS1_PY = r'>>>' | |||
|
272 | _PS2_PY = r'\.\.\.' | |||
|
273 | ||||
|
274 | _PS1_IP = r'In\ \[\d+\]:' | |||
|
275 | _PS2_IP = r'\ \ \ \.\.\.+:' | |||
|
276 | ||||
|
277 | _RE_TPL = r''' | |||
|
278 | # Source consists of a PS1 line followed by zero or more PS2 lines. | |||
|
279 | (?P<source> | |||
|
280 | (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line | |||
|
281 | (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines | |||
|
282 | \n? # a newline | |||
|
283 | # Want consists of any non-blank lines that do not start with PS1. | |||
|
284 | (?P<want> (?:(?![ ]*$) # Not a blank line | |||
|
285 | (?![ ]*%s) # Not a line starting with PS1 | |||
|
286 | (?![ ]*%s) # Not a line starting with PS2 | |||
|
287 | .*$\n? # But any other line | |||
|
288 | )*) | |||
|
289 | ''' | |||
|
290 | ||||
|
291 | _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY), | |||
|
292 | re.MULTILINE | re.VERBOSE) | |||
|
293 | ||||
|
294 | _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP), | |||
|
295 | re.MULTILINE | re.VERBOSE) | |||
|
296 | ||||
|
297 | def ip2py(self,source): | |||
|
298 | """Convert input IPython source into valid Python.""" | |||
|
299 | out = [] | |||
|
300 | newline = out.append | |||
|
301 | for line in source.splitlines(): | |||
|
302 | #newline(_ip.IPipython.prefilter(line,True)) | |||
|
303 | newline(_ip.IP.prefilter(line,True)) | |||
|
304 | newline('') # ensure a closing newline, needed by doctest | |||
|
305 | return '\n'.join(out) | |||
|
306 | ||||
|
307 | def parse(self, string, name='<string>'): | |||
|
308 | """ | |||
|
309 | Divide the given string into examples and intervening text, | |||
|
310 | and return them as a list of alternating Examples and strings. | |||
|
311 | Line numbers for the Examples are 0-based. The optional | |||
|
312 | argument `name` is a name identifying this string, and is only | |||
|
313 | used for error messages. | |||
|
314 | """ | |||
|
315 | ||||
|
316 | #print 'Parse string:\n',string # dbg | |||
|
317 | ||||
|
318 | string = string.expandtabs() | |||
|
319 | # If all lines begin with the same indentation, then strip it. | |||
|
320 | min_indent = self._min_indent(string) | |||
|
321 | if min_indent > 0: | |||
|
322 | string = '\n'.join([l[min_indent:] for l in string.split('\n')]) | |||
|
323 | ||||
|
324 | output = [] | |||
|
325 | charno, lineno = 0, 0 | |||
|
326 | ||||
|
327 | # Whether to convert the input from ipython to python syntax | |||
|
328 | ip2py = False | |||
|
329 | # Find all doctest examples in the string. First, try them as Python | |||
|
330 | # examples, then as IPython ones | |||
|
331 | terms = list(self._EXAMPLE_RE_PY.finditer(string)) | |||
|
332 | if terms: | |||
|
333 | # Normal Python example | |||
|
334 | #print '-'*70 # dbg | |||
|
335 | #print 'PyExample, Source:\n',string # dbg | |||
|
336 | #print '-'*70 # dbg | |||
|
337 | Example = doctest.Example | |||
|
338 | else: | |||
|
339 | # It's an ipython example. Note that IPExamples are run | |||
|
340 | # in-process, so their syntax must be turned into valid python. | |||
|
341 | # IPExternalExamples are run out-of-process (via pexpect) so they | |||
|
342 | # don't need any filtering (a real ipython will be executing them). | |||
|
343 | terms = list(self._EXAMPLE_RE_IP.finditer(string)) | |||
|
344 | if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string): | |||
|
345 | #print '-'*70 # dbg | |||
|
346 | #print 'IPExternalExample, Source:\n',string # dbg | |||
|
347 | #print '-'*70 # dbg | |||
|
348 | Example = IPExternalExample | |||
|
349 | else: | |||
|
350 | #print '-'*70 # dbg | |||
|
351 | #print 'IPExample, Source:\n',string # dbg | |||
|
352 | #print '-'*70 # dbg | |||
|
353 | Example = IPExample | |||
|
354 | ip2py = True | |||
|
355 | ||||
|
356 | for m in terms: | |||
|
357 | # Add the pre-example text to `output`. | |||
|
358 | output.append(string[charno:m.start()]) | |||
|
359 | # Update lineno (lines before this example) | |||
|
360 | lineno += string.count('\n', charno, m.start()) | |||
|
361 | # Extract info from the regexp match. | |||
|
362 | (source, options, want, exc_msg) = \ | |||
|
363 | self._parse_example(m, name, lineno,ip2py) | |||
|
364 | if Example is IPExternalExample: | |||
|
365 | options[doctest.NORMALIZE_WHITESPACE] = True | |||
|
366 | want += '\n' | |||
|
367 | # Create an Example, and add it to the list. | |||
|
368 | if not self._IS_BLANK_OR_COMMENT(source): | |||
|
369 | #print 'Example source:', source # dbg | |||
|
370 | output.append(Example(source, want, exc_msg, | |||
|
371 | lineno=lineno, | |||
|
372 | indent=min_indent+len(m.group('indent')), | |||
|
373 | options=options)) | |||
|
374 | # Update lineno (lines inside this example) | |||
|
375 | lineno += string.count('\n', m.start(), m.end()) | |||
|
376 | # Update charno. | |||
|
377 | charno = m.end() | |||
|
378 | # Add any remaining post-example text to `output`. | |||
|
379 | output.append(string[charno:]) | |||
|
380 | ||||
|
381 | return output | |||
|
382 | ||||
|
383 | def _parse_example(self, m, name, lineno,ip2py=False): | |||
|
384 | """ | |||
|
385 | Given a regular expression match from `_EXAMPLE_RE` (`m`), | |||
|
386 | return a pair `(source, want)`, where `source` is the matched | |||
|
387 | example's source code (with prompts and indentation stripped); | |||
|
388 | and `want` is the example's expected output (with indentation | |||
|
389 | stripped). | |||
|
390 | ||||
|
391 | `name` is the string's name, and `lineno` is the line number | |||
|
392 | where the example starts; both are used for error messages. | |||
|
393 | ||||
|
394 | Optional: | |||
|
395 | `ip2py`: if true, filter the input via IPython to convert the syntax | |||
|
396 | into valid python. | |||
|
397 | """ | |||
|
398 | ||||
|
399 | # Get the example's indentation level. | |||
|
400 | indent = len(m.group('indent')) | |||
|
401 | ||||
|
402 | # Divide source into lines; check that they're properly | |||
|
403 | # indented; and then strip their indentation & prompts. | |||
|
404 | source_lines = m.group('source').split('\n') | |||
|
405 | ||||
|
406 | # We're using variable-length input prompts | |||
|
407 | ps1 = m.group('ps1') | |||
|
408 | ps2 = m.group('ps2') | |||
|
409 | ps1_len = len(ps1) | |||
|
410 | ||||
|
411 | self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len) | |||
|
412 | if ps2: | |||
|
413 | self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno) | |||
|
414 | ||||
|
415 | source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines]) | |||
|
416 | ||||
|
417 | if ip2py: | |||
|
418 | # Convert source input from IPython into valid Python syntax | |||
|
419 | source = self.ip2py(source) | |||
|
420 | ||||
|
421 | # Divide want into lines; check that it's properly indented; and | |||
|
422 | # then strip the indentation. Spaces before the last newline should | |||
|
423 | # be preserved, so plain rstrip() isn't good enough. | |||
|
424 | want = m.group('want') | |||
|
425 | want_lines = want.split('\n') | |||
|
426 | if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): | |||
|
427 | del want_lines[-1] # forget final newline & spaces after it | |||
|
428 | self._check_prefix(want_lines, ' '*indent, name, | |||
|
429 | lineno + len(source_lines)) | |||
|
430 | ||||
|
431 | # Remove ipython output prompt that might be present in the first line | |||
|
432 | want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0]) | |||
|
433 | ||||
|
434 | want = '\n'.join([wl[indent:] for wl in want_lines]) | |||
|
435 | ||||
|
436 | # If `want` contains a traceback message, then extract it. | |||
|
437 | m = self._EXCEPTION_RE.match(want) | |||
|
438 | if m: | |||
|
439 | exc_msg = m.group('msg') | |||
|
440 | else: | |||
|
441 | exc_msg = None | |||
|
442 | ||||
|
443 | # Extract options from the source. | |||
|
444 | options = self._find_options(source, name, lineno) | |||
|
445 | ||||
|
446 | return source, options, want, exc_msg | |||
|
447 | ||||
|
448 | def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len): | |||
|
449 | """ | |||
|
450 | Given the lines of a source string (including prompts and | |||
|
451 | leading indentation), check to make sure that every prompt is | |||
|
452 | followed by a space character. If any line is not followed by | |||
|
453 | a space character, then raise ValueError. | |||
|
454 | ||||
|
455 | Note: IPython-modified version which takes the input prompt length as a | |||
|
456 | parameter, so that prompts of variable length can be dealt with. | |||
|
457 | """ | |||
|
458 | space_idx = indent+ps1_len | |||
|
459 | min_len = space_idx+1 | |||
|
460 | for i, line in enumerate(lines): | |||
|
461 | if len(line) >= min_len and line[space_idx] != ' ': | |||
|
462 | raise ValueError('line %r of the docstring for %s ' | |||
|
463 | 'lacks blank after %s: %r' % | |||
|
464 | (lineno+i+1, name, | |||
|
465 | line[indent:space_idx], line)) | |||
|
466 | ||||
|
467 | SKIP = doctest.register_optionflag('SKIP') | |||
|
468 | ||||
|
469 | ########################################################################### | |||
|
470 | ||||
|
471 | class DocFileCase(doctest.DocFileCase): | |||
|
472 | """Overrides to provide filename | |||
|
473 | """ | |||
|
474 | def address(self): | |||
|
475 | return (self._dt_test.filename, None, None) | |||
|
476 | ||||
|
477 | ||||
|
478 | class ExtensionDoctest(doctests.Doctest): | |||
|
479 | """Nose Plugin that supports doctests in extension modules. | |||
|
480 | """ | |||
|
481 | name = 'extdoctest' # call nosetests with --with-extdoctest | |||
|
482 | enabled = True | |||
|
483 | ||||
|
484 | def options(self, parser, env=os.environ): | |||
|
485 | Plugin.options(self, parser, env) | |||
|
486 | ||||
|
487 | def configure(self, options, config): | |||
|
488 | Plugin.configure(self, options, config) | |||
|
489 | self.doctest_tests = options.doctest_tests | |||
|
490 | self.extension = tolist(options.doctestExtension) | |||
|
491 | self.finder = DocTestFinder() | |||
|
492 | self.parser = doctest.DocTestParser() | |||
|
493 | ||||
|
494 | ||||
|
495 | def loadTestsFromExtensionModule(self,filename): | |||
|
496 | bpath,mod = os.path.split(filename) | |||
|
497 | modname = os.path.splitext(mod)[0] | |||
|
498 | try: | |||
|
499 | sys.path.append(bpath) | |||
|
500 | module = __import__(modname) | |||
|
501 | tests = list(self.loadTestsFromModule(module)) | |||
|
502 | finally: | |||
|
503 | sys.path.pop() | |||
|
504 | return tests | |||
|
505 | ||||
|
506 | def loadTestsFromFile(self, filename): | |||
|
507 | if is_extension_module(filename): | |||
|
508 | for t in self.loadTestsFromExtensionModule(filename): | |||
|
509 | yield t | |||
|
510 | else: | |||
|
511 | ## for t in list(doctests.Doctest.loadTestsFromFile(self,filename)): | |||
|
512 | ## yield t | |||
|
513 | pass | |||
|
514 | ||||
|
515 | if self.extension and anyp(filename.endswith, self.extension): | |||
|
516 | #print 'lTF',filename # dbg | |||
|
517 | name = os.path.basename(filename) | |||
|
518 | dh = open(filename) | |||
|
519 | try: | |||
|
520 | doc = dh.read() | |||
|
521 | finally: | |||
|
522 | dh.close() | |||
|
523 | test = self.parser.get_doctest( | |||
|
524 | doc, globs={'__file__': filename}, name=name, | |||
|
525 | filename=filename, lineno=0) | |||
|
526 | if test.examples: | |||
|
527 | #print 'FileCase:',test.examples # dbg | |||
|
528 | yield DocFileCase(test) | |||
|
529 | else: | |||
|
530 | yield False # no tests to load | |||
|
531 | ||||
|
532 | ||||
|
533 | def wantFile(self,filename): | |||
|
534 | """Return whether the given filename should be scanned for tests. | |||
|
535 | ||||
|
536 | Modified version that accepts extension modules as valid containers for | |||
|
537 | doctests. | |||
|
538 | """ | |||
|
539 | #print 'Filename:',filename # dbg | |||
|
540 | ||||
|
541 | if is_extension_module(filename): | |||
|
542 | return True | |||
|
543 | else: | |||
|
544 | return doctests.Doctest.wantFile(self,filename) | |||
|
545 | ||||
|
546 | # NOTE: the method below is a *copy* of the one in the nose doctests | |||
|
547 | # plugin, but we have to replicate it here in order to have it resolve the | |||
|
548 | # DocTestCase (last line) to our local copy, since the nose plugin doesn't | |||
|
549 | # provide a public hook for what TestCase class to use. The alternative | |||
|
550 | # would be to monkeypatch doctest in the stdlib, but that's ugly and | |||
|
551 | # brittle, since a change in plugin load order can break it. So for now, | |||
|
552 | # we just paste this in here, inelegant as this may be. | |||
|
553 | ||||
|
554 | def loadTestsFromModule(self, module): | |||
|
555 | #print 'lTM',module # dbg | |||
|
556 | ||||
|
557 | if not self.matches(module.__name__): | |||
|
558 | log.debug("Doctest doesn't want module %s", module) | |||
|
559 | return | |||
|
560 | tests = self.finder.find(module) | |||
|
561 | if not tests: | |||
|
562 | return | |||
|
563 | tests.sort() | |||
|
564 | module_file = module.__file__ | |||
|
565 | if module_file[-4:] in ('.pyc', '.pyo'): | |||
|
566 | module_file = module_file[:-1] | |||
|
567 | for test in tests: | |||
|
568 | if not test.examples: | |||
|
569 | continue | |||
|
570 | if not test.filename: | |||
|
571 | test.filename = module_file | |||
|
572 | yield DocTestCase(test) | |||
|
573 | ||||
|
574 | class IPythonDoctest(ExtensionDoctest): | |||
|
575 | """Nose Plugin that supports doctests in extension modules. | |||
|
576 | """ | |||
|
577 | name = 'ipdoctest' # call nosetests with --with-ipdoctest | |||
|
578 | enabled = True | |||
|
579 | ||||
|
580 | def configure(self, options, config): | |||
|
581 | ||||
|
582 | Plugin.configure(self, options, config) | |||
|
583 | self.doctest_tests = options.doctest_tests | |||
|
584 | self.extension = tolist(options.doctestExtension) | |||
|
585 | self.parser = IPDocTestParser() | |||
|
586 | #self.finder = DocTestFinder(parser=IPDocTestParser()) | |||
|
587 | self.finder = DocTestFinder(parser=self.parser) |
@@ -0,0 +1,18 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | """Nose-based test runner. | |||
|
3 | """ | |||
|
4 | ||||
|
5 | from nose.core import main | |||
|
6 | from nose.plugins.builtin import plugins | |||
|
7 | from nose.plugins.doctests import Doctest | |||
|
8 | ||||
|
9 | import ipdoctest | |||
|
10 | from ipdoctest import IPDocTestRunner | |||
|
11 | ||||
|
12 | if __name__ == '__main__': | |||
|
13 | print 'WARNING: this code is incomplete!' | |||
|
14 | ||||
|
15 | ||||
|
16 | pp = [x() for x in plugins] # activate all builtin plugins first | |||
|
17 | main(testRunner=IPDocTestRunner(), | |||
|
18 | plugins=pp+[ipdoctest.IPythonDoctest(),Doctest()]) |
@@ -0,0 +1,18 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | """A Nose plugin to support IPython doctests. | |||
|
3 | """ | |||
|
4 | ||||
|
5 | from setuptools import setup | |||
|
6 | ||||
|
7 | setup(name='IPython doctest plugin', | |||
|
8 | version='0.1', | |||
|
9 | author='The IPython Team', | |||
|
10 | description = 'Nose plugin to load IPython-extended doctests', | |||
|
11 | license = 'LGPL', | |||
|
12 | py_modules = ['ipdoctest'], | |||
|
13 | entry_points = { | |||
|
14 | 'nose.plugins.0.10': ['ipdoctest = ipdoctest:IPythonDoctest', | |||
|
15 | 'extdoctest = ipdoctest:ExtensionDoctest', | |||
|
16 | ], | |||
|
17 | }, | |||
|
18 | ) |
@@ -0,0 +1,36 b'' | |||||
|
1 | ======================= | |||
|
2 | Combo testing example | |||
|
3 | ======================= | |||
|
4 | ||||
|
5 | This is a simple example that mixes ipython doctests:: | |||
|
6 | ||||
|
7 | In [1]: import code | |||
|
8 | ||||
|
9 | In [2]: 2**12 | |||
|
10 | Out[2]: 4096 | |||
|
11 | ||||
|
12 | with command-line example information that does *not* get executed:: | |||
|
13 | ||||
|
14 | $ mpirun -n 4 ipengine --controller-port=10000 --controller-ip=host0 | |||
|
15 | ||||
|
16 | and with literal examples of Python source code:: | |||
|
17 | ||||
|
18 | controller = dict(host='myhost', | |||
|
19 | engine_port=None, # default is 10105 | |||
|
20 | control_port=None, | |||
|
21 | ) | |||
|
22 | ||||
|
23 | # keys are hostnames, values are the number of engine on that host | |||
|
24 | engines = dict(node1=2, | |||
|
25 | node2=2, | |||
|
26 | node3=2, | |||
|
27 | node3=2, | |||
|
28 | ) | |||
|
29 | ||||
|
30 | # Force failure to detect that this test is being run. | |||
|
31 | 1/0 | |||
|
32 | ||||
|
33 | These source code examples are executed but no output is compared at all. An | |||
|
34 | error or failure is reported only if an exception is raised. | |||
|
35 | ||||
|
36 | NOTE: the execution of pure python blocks is not yet working! |
@@ -0,0 +1,24 b'' | |||||
|
1 | ===================================== | |||
|
2 | Tests in example form - pure python | |||
|
3 | ===================================== | |||
|
4 | ||||
|
5 | This file contains doctest examples embedded as code blocks, using normal | |||
|
6 | Python prompts. See the accompanying file for similar examples using IPython | |||
|
7 | prompts (you can't mix both types within one file). The following will be run | |||
|
8 | as a test:: | |||
|
9 | ||||
|
10 | >>> 1+1 | |||
|
11 | 2 | |||
|
12 | >>> print "hello" | |||
|
13 | hello | |||
|
14 | ||||
|
15 | More than one example works:: | |||
|
16 | ||||
|
17 | >>> s="Hello World" | |||
|
18 | ||||
|
19 | >>> s.upper() | |||
|
20 | 'HELLO WORLD' | |||
|
21 | ||||
|
22 | but you should note that the *entire* test file is considered to be a single | |||
|
23 | test. Individual code blocks that fail are printed separately as ``example | |||
|
24 | failures``, but the whole file is still counted and reported as one test. |
@@ -0,0 +1,30 b'' | |||||
|
1 | ================================= | |||
|
2 | Tests in example form - IPython | |||
|
3 | ================================= | |||
|
4 | ||||
|
5 | You can write text files with examples that use IPython prompts (as long as you | |||
|
6 | use the nose ipython doctest plugin), but you can not mix and match prompt | |||
|
7 | styles in a single file. That is, you either use all ``>>>`` prompts or all | |||
|
8 | IPython-style prompts. Your test suite *can* have both types, you just need to | |||
|
9 | put each type of example in a separate. Using IPython prompts, you can paste | |||
|
10 | directly from your session:: | |||
|
11 | ||||
|
12 | In [5]: s="Hello World" | |||
|
13 | ||||
|
14 | In [6]: s.upper() | |||
|
15 | Out[6]: 'HELLO WORLD' | |||
|
16 | ||||
|
17 | Another example:: | |||
|
18 | ||||
|
19 | In [8]: 1+3 | |||
|
20 | Out[8]: 4 | |||
|
21 | ||||
|
22 | Just like in IPython docstrings, you can use all IPython syntax and features:: | |||
|
23 | ||||
|
24 | In [9]: !echo "hello" | |||
|
25 | hello | |||
|
26 | ||||
|
27 | In [10]: a='hi' | |||
|
28 | ||||
|
29 | In [11]: !echo $a | |||
|
30 | hi |
1 | NO CONTENT: new file 100644, binary diff hidden |
|
NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,23 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | ||||
|
4 | # A super simple example showing how to use all of this in a fully | |||
|
5 | # asynchronous manner. The TaskClient also works in this mode. | |||
|
6 | ||||
|
7 | from twisted.internet import reactor, defer | |||
|
8 | from IPython.kernel import asyncclient | |||
|
9 | ||||
|
10 | def printer(r): | |||
|
11 | print r | |||
|
12 | return r | |||
|
13 | ||||
|
14 | def submit(client): | |||
|
15 | d = client.push(dict(a=5, b='asdf', c=[1,2,3]),targets=0,block=True) | |||
|
16 | d.addCallback(lambda _: client.pull(('a','b','c'),targets=0,block=True)) | |||
|
17 | d.addBoth(printer) | |||
|
18 | d.addCallback(lambda _: reactor.stop()) | |||
|
19 | ||||
|
20 | d = asyncclient.get_multiengine_client() | |||
|
21 | d.addCallback(submit) | |||
|
22 | ||||
|
23 | reactor.run() No newline at end of file |
@@ -0,0 +1,32 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | ||||
|
4 | # This example shows how the AsynTaskClient can be used | |||
|
5 | # This example is currently broken | |||
|
6 | ||||
|
7 | from twisted.internet import reactor, defer | |||
|
8 | from IPython.kernel import asyncclient | |||
|
9 | ||||
|
10 | mec = asyncclient.AsyncMultiEngineClient(('localhost', 10105)) | |||
|
11 | tc = asyncclient.AsyncTaskClient(('localhost',10113)) | |||
|
12 | ||||
|
13 | cmd1 = """\ | |||
|
14 | a = 5 | |||
|
15 | b = 10*d | |||
|
16 | c = a*b*d | |||
|
17 | """ | |||
|
18 | ||||
|
19 | t1 = asyncclient.Task(cmd1, clear_before=False, clear_after=True, pull=['a','b','c']) | |||
|
20 | ||||
|
21 | d = mec.push(dict(d=30)) | |||
|
22 | ||||
|
23 | def raise_and_print(tr): | |||
|
24 | tr.raiseException() | |||
|
25 | print "a, b: ", tr.ns.a, tr.ns.b | |||
|
26 | return tr | |||
|
27 | ||||
|
28 | d.addCallback(lambda _: tc.run(t1)) | |||
|
29 | d.addCallback(lambda tid: tc.get_task_result(tid,block=True)) | |||
|
30 | d.addCallback(raise_and_print) | |||
|
31 | d.addCallback(lambda _: reactor.stop()) | |||
|
32 | reactor.run() |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 | ||
The requested commit or file is too big and content was truncated. Show full diff |
@@ -2728,8 +2728,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
2728 | os.chdir(os.path.expanduser(ps)) |
|
2728 | os.chdir(os.path.expanduser(ps)) | |
2729 | if self.shell.rc.term_title: |
|
2729 | if self.shell.rc.term_title: | |
2730 | #print 'set term title:',self.shell.rc.term_title # dbg |
|
2730 | #print 'set term title:',self.shell.rc.term_title # dbg | |
2731 |
|
|
2731 | platutils.set_term_title('IPy ' + abbrev_cwd()) | |
2732 | platutils.set_term_title(ttitle) |
|
|||
2733 | except OSError: |
|
2732 | except OSError: | |
2734 | print sys.exc_info()[1] |
|
2733 | print sys.exc_info()[1] | |
2735 | else: |
|
2734 | else: | |
@@ -3195,7 +3194,7 b' Defaulting color scheme to \'NoColor\'"""' | |||||
3195 | exec b in self.user_ns |
|
3194 | exec b in self.user_ns | |
3196 | self.user_ns['pasted_block'] = b |
|
3195 | self.user_ns['pasted_block'] = b | |
3197 | else: |
|
3196 | else: | |
3198 | self.user_ns[par] = block |
|
3197 | self.user_ns[par] = SList(block.splitlines()) | |
3199 | print "Block assigned to '%s'" % par |
|
3198 | print "Block assigned to '%s'" % par | |
3200 |
|
3199 | |||
3201 | def magic_quickref(self,arg): |
|
3200 | def magic_quickref(self,arg): |
@@ -22,15 +22,19 b" name = 'ipython'" | |||||
22 | # because bdist_rpm does not accept dashes (an RPM) convention, and |
|
22 | # because bdist_rpm does not accept dashes (an RPM) convention, and | |
23 | # bdist_deb does not accept underscores (a Debian convention). |
|
23 | # bdist_deb does not accept underscores (a Debian convention). | |
24 |
|
24 | |||
25 | revision = '1016' |
|
25 | development = True # change this to False to do a release | |
|
26 | version_base = '0.9.0' | |||
26 | branch = 'ipython' |
|
27 | branch = 'ipython' | |
|
28 | revision = '1016' | |||
27 |
|
29 | |||
28 | if branch == 'ipython': |
|
30 | if development: | |
29 | version = '0.9.0.bzr.r' + revision |
|
31 | if branch == 'ipython': | |
|
32 | version = '%s.bzr.r%s' % (version_base, revision) | |||
|
33 | else: | |||
|
34 | version = '%s.bzr.r%s.%s' % (version_base, revision, branch) | |||
30 | else: |
|
35 | else: | |
31 | version = '0.9.0.bzr.r%s.%s' % (revision,branch) |
|
36 | version = version_base | |
32 |
|
37 | |||
33 | # version = '0.8.4' |
|
|||
34 |
|
38 | |||
35 | description = "Tools for interactive development in Python." |
|
39 | description = "Tools for interactive development in Python." | |
36 |
|
40 |
@@ -53,7 +53,7 b' globsyntax = """\\' | |||||
53 | - readme*, exclude files ending with .bak |
|
53 | - readme*, exclude files ending with .bak | |
54 | !.svn/ !.hg/ !*_Data/ rec:. |
|
54 | !.svn/ !.hg/ !*_Data/ rec:. | |
55 | - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse. |
|
55 | - Skip .svn, .hg, foo_Data dirs (and their subdirs) in recurse. | |
56 | Trailing / is the key, \ does not work! |
|
56 | Trailing / is the key, \ does not work! Use !.*/ for all hidden. | |
57 | dir:foo |
|
57 | dir:foo | |
58 | - the directory foo if it exists (not files in foo) |
|
58 | - the directory foo if it exists (not files in foo) | |
59 | dir:* |
|
59 | dir:* | |
@@ -63,13 +63,16 b' globsyntax = """\\' | |||||
63 | foo.py is *not* included twice. |
|
63 | foo.py is *not* included twice. | |
64 | @filelist.txt |
|
64 | @filelist.txt | |
65 | - All files listed in 'filelist.txt' file, on separate lines. |
|
65 | - All files listed in 'filelist.txt' file, on separate lines. | |
|
66 | "cont:class \wak:" rec:*.py | |||
|
67 | - Match files containing regexp. Applies to subsequent files. | |||
|
68 | note quotes because of whitespace. | |||
66 | """ |
|
69 | """ | |
67 |
|
70 | |||
68 |
|
71 | |||
69 | __version__ = "0.2" |
|
72 | __version__ = "0.2" | |
70 |
|
73 | |||
71 |
|
74 | |||
72 | import os,glob,fnmatch,sys |
|
75 | import os,glob,fnmatch,sys,re | |
73 | from sets import Set as set |
|
76 | from sets import Set as set | |
74 |
|
77 | |||
75 |
|
78 | |||
@@ -84,21 +87,34 b' def expand(flist,exp_dirs = False):' | |||||
84 |
|
87 | |||
85 | """ |
|
88 | """ | |
86 | if isinstance(flist, basestring): |
|
89 | if isinstance(flist, basestring): | |
87 | flist = flist.split() |
|
90 | import shlex | |
|
91 | flist = shlex.split(flist) | |||
88 | done_set = set() |
|
92 | done_set = set() | |
89 | denied_set = set() |
|
93 | denied_set = set() | |
90 |
|
94 | cont_set = set() | ||
|
95 | cur_rejected_dirs = set() | |||
|
96 | ||||
91 | def recfind(p, pats = ["*"]): |
|
97 | def recfind(p, pats = ["*"]): | |
92 |
denied_dirs = [ |
|
98 | denied_dirs = [os.path.dirname(d) for d in denied_set if d.endswith("/")] | |
93 | #print "de", denied_dirs |
|
|||
94 | for (dp,dnames,fnames) in os.walk(p): |
|
99 | for (dp,dnames,fnames) in os.walk(p): | |
95 | # see if we should ignore the whole directory |
|
100 | # see if we should ignore the whole directory | |
96 | dp_norm = dp.replace("\\","/") + "/" |
|
101 | dp_norm = dp.replace("\\","/") + "/" | |
97 | deny = False |
|
102 | deny = False | |
|
103 | # do not traverse under already rejected dirs | |||
|
104 | for d in cur_rejected_dirs: | |||
|
105 | if dp.startswith(d): | |||
|
106 | deny = True | |||
|
107 | break | |||
|
108 | if deny: | |||
|
109 | continue | |||
|
110 | ||||
|
111 | ||||
98 | #print "dp",dp |
|
112 | #print "dp",dp | |
|
113 | bname = os.path.basename(dp) | |||
99 | for deny_pat in denied_dirs: |
|
114 | for deny_pat in denied_dirs: | |
100 |
if fnmatch.fnmatch( |
|
115 | if fnmatch.fnmatch( bname, deny_pat): | |
101 | deny = True |
|
116 | deny = True | |
|
117 | cur_rejected_dirs.add(dp) | |||
102 | break |
|
118 | break | |
103 | if deny: |
|
119 | if deny: | |
104 | continue |
|
120 | continue | |
@@ -124,6 +140,17 b' def expand(flist,exp_dirs = False):' | |||||
124 | if fnmatch.fnmatch(os.path.basename(p), deny_pat): |
|
140 | if fnmatch.fnmatch(os.path.basename(p), deny_pat): | |
125 | deny = True |
|
141 | deny = True | |
126 | break |
|
142 | break | |
|
143 | if cont_set: | |||
|
144 | try: | |||
|
145 | cont = open(p).read() | |||
|
146 | except IOError: | |||
|
147 | # deny | |||
|
148 | continue | |||
|
149 | for pat in cont_set: | |||
|
150 | if not re.search(pat,cont, re.IGNORECASE): | |||
|
151 | deny = True | |||
|
152 | break | |||
|
153 | ||||
127 | if not deny: |
|
154 | if not deny: | |
128 | yield it |
|
155 | yield it | |
129 | return |
|
156 | return | |
@@ -158,7 +185,8 b' def expand(flist,exp_dirs = False):' | |||||
158 | # glob only dirs |
|
185 | # glob only dirs | |
159 | elif ent.lower().startswith('dir:'): |
|
186 | elif ent.lower().startswith('dir:'): | |
160 | res.extend(once_filter(filter(os.path.isdir,glob.glob(ent[4:])))) |
|
187 | res.extend(once_filter(filter(os.path.isdir,glob.glob(ent[4:])))) | |
161 |
|
188 | elif ent.lower().startswith('cont:'): | ||
|
189 | cont_set.add(ent[5:]) | |||
162 | # get all files in the specified dir |
|
190 | # get all files in the specified dir | |
163 | elif os.path.isdir(ent) and exp_dirs: |
|
191 | elif os.path.isdir(ent) and exp_dirs: | |
164 | res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*")))) |
|
192 | res.extend(once_filter(filter(os.path.isfile,glob.glob(ent + os.sep+"*")))) |
@@ -24,12 +24,14 b' __docformat__ = "restructuredtext en"' | |||||
24 | # Imports |
|
24 | # Imports | |
25 | #----------------------------------------------------------------------------- |
|
25 | #----------------------------------------------------------------------------- | |
26 |
|
26 | |||
|
27 | import sys | |||
27 | import objc |
|
28 | import objc | |
28 | import uuid |
|
29 | import uuid | |
29 |
|
30 | |||
30 | from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\ |
|
31 | from Foundation import NSObject, NSMutableArray, NSMutableDictionary,\ | |
31 | NSLog, NSNotificationCenter, NSMakeRange,\ |
|
32 | NSLog, NSNotificationCenter, NSMakeRange,\ | |
32 | NSLocalizedString, NSIntersectionRange |
|
33 | NSLocalizedString, NSIntersectionRange,\ | |
|
34 | NSString, NSAutoreleasePool | |||
33 |
|
35 | |||
34 | from AppKit import NSApplicationWillTerminateNotification, NSBeep,\ |
|
36 | from AppKit import NSApplicationWillTerminateNotification, NSBeep,\ | |
35 | NSTextView, NSRulerView, NSVerticalRuler |
|
37 | NSTextView, NSRulerView, NSVerticalRuler | |
@@ -38,7 +40,7 b' from pprint import saferepr' | |||||
38 |
|
40 | |||
39 | import IPython |
|
41 | import IPython | |
40 | from IPython.kernel.engineservice import ThreadedEngineService |
|
42 | from IPython.kernel.engineservice import ThreadedEngineService | |
41 | from IPython.frontend.frontendbase import FrontEndBase |
|
43 | from IPython.frontend.frontendbase import AsyncFrontEndBase | |
42 |
|
44 | |||
43 | from twisted.internet.threads import blockingCallFromThread |
|
45 | from twisted.internet.threads import blockingCallFromThread | |
44 | from twisted.python.failure import Failure |
|
46 | from twisted.python.failure import Failure | |
@@ -52,17 +54,57 b' from twisted.python.failure import Failure' | |||||
52 | # ThreadedEngineService? |
|
54 | # ThreadedEngineService? | |
53 | # 2. integrate Xgrid launching of engines |
|
55 | # 2. integrate Xgrid launching of engines | |
54 |
|
56 | |||
|
57 | class AutoreleasePoolWrappedThreadedEngineService(ThreadedEngineService): | |||
|
58 | """Wrap all blocks in an NSAutoreleasePool""" | |||
55 |
|
59 | |||
|
60 | def wrapped_execute(self, msg, lines): | |||
|
61 | """wrapped_execute""" | |||
|
62 | try: | |||
|
63 | p = NSAutoreleasePool.alloc().init() | |||
|
64 | result = self.shell.execute(lines) | |||
|
65 | except Exception,e: | |||
|
66 | # This gives the following: | |||
|
67 | # et=exception class | |||
|
68 | # ev=exception class instance | |||
|
69 | # tb=traceback object | |||
|
70 | et,ev,tb = sys.exc_info() | |||
|
71 | # This call adds attributes to the exception value | |||
|
72 | et,ev,tb = self.shell.formatTraceback(et,ev,tb,msg) | |||
|
73 | # Add another attribute | |||
|
74 | ||||
|
75 | # Create a new exception with the new attributes | |||
|
76 | e = et(ev._ipython_traceback_text) | |||
|
77 | e._ipython_engine_info = msg | |||
|
78 | ||||
|
79 | # Re-raise | |||
|
80 | raise e | |||
|
81 | finally: | |||
|
82 | p.drain() | |||
|
83 | ||||
|
84 | return result | |||
|
85 | ||||
|
86 | def execute(self, lines): | |||
|
87 | # Only import this if we are going to use this class | |||
|
88 | from twisted.internet import threads | |||
|
89 | ||||
|
90 | msg = {'engineid':self.id, | |||
|
91 | 'method':'execute', | |||
|
92 | 'args':[lines]} | |||
|
93 | ||||
|
94 | d = threads.deferToThread(self.wrapped_execute, msg, lines) | |||
|
95 | d.addCallback(self.addIDToResult) | |||
|
96 | return d | |||
56 |
|
97 | |||
57 |
|
98 | |||
58 | class IPythonCocoaController(NSObject, FrontEndBase): |
|
99 | class IPythonCocoaController(NSObject, AsyncFrontEndBase): | |
59 | userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value)) |
|
100 | userNS = objc.ivar() #mirror of engine.user_ns (key=>str(value)) | |
60 | waitingForEngine = objc.ivar().bool() |
|
101 | waitingForEngine = objc.ivar().bool() | |
61 | textView = objc.IBOutlet() |
|
102 | textView = objc.IBOutlet() | |
62 |
|
103 | |||
63 | def init(self): |
|
104 | def init(self): | |
64 | self = super(IPythonCocoaController, self).init() |
|
105 | self = super(IPythonCocoaController, self).init() | |
65 |
FrontEndBase.__init__(self, |
|
106 | AsyncFrontEndBase.__init__(self, | |
|
107 | engine=AutoreleasePoolWrappedThreadedEngineService()) | |||
66 | if(self != None): |
|
108 | if(self != None): | |
67 | self._common_init() |
|
109 | self._common_init() | |
68 |
|
110 | |||
@@ -133,13 +175,43 b' class IPythonCocoaController(NSObject, FrontEndBase):' | |||||
133 | def execute(self, block, blockID=None): |
|
175 | def execute(self, block, blockID=None): | |
134 | self.waitingForEngine = True |
|
176 | self.waitingForEngine = True | |
135 | self.willChangeValueForKey_('commandHistory') |
|
177 | self.willChangeValueForKey_('commandHistory') | |
136 |
d = super(IPythonCocoaController, self).execute(block, |
|
178 | d = super(IPythonCocoaController, self).execute(block, | |
|
179 | blockID) | |||
137 | d.addBoth(self._engine_done) |
|
180 | d.addBoth(self._engine_done) | |
138 | d.addCallback(self._update_user_ns) |
|
181 | d.addCallback(self._update_user_ns) | |
139 |
|
182 | |||
140 | return d |
|
183 | return d | |
141 |
|
184 | |||
|
185 | ||||
|
186 | def push_(self, namespace): | |||
|
187 | """Push dictionary of key=>values to python namespace""" | |||
142 |
|
188 | |||
|
189 | self.waitingForEngine = True | |||
|
190 | self.willChangeValueForKey_('commandHistory') | |||
|
191 | d = self.engine.push(namespace) | |||
|
192 | d.addBoth(self._engine_done) | |||
|
193 | d.addCallback(self._update_user_ns) | |||
|
194 | ||||
|
195 | ||||
|
196 | def pull_(self, keys): | |||
|
197 | """Pull keys from python namespace""" | |||
|
198 | ||||
|
199 | self.waitingForEngine = True | |||
|
200 | result = blockingCallFromThread(self.engine.pull, keys) | |||
|
201 | self.waitingForEngine = False | |||
|
202 | ||||
|
203 | @objc.signature('v@:@I') | |||
|
204 | def executeFileAtPath_encoding_(self, path, encoding): | |||
|
205 | """Execute file at path in an empty namespace. Update the engine | |||
|
206 | user_ns with the resulting locals.""" | |||
|
207 | ||||
|
208 | lines,err = NSString.stringWithContentsOfFile_encoding_error_( | |||
|
209 | path, | |||
|
210 | encoding, | |||
|
211 | None) | |||
|
212 | self.engine.execute(lines) | |||
|
213 | ||||
|
214 | ||||
143 | def _engine_done(self, x): |
|
215 | def _engine_done(self, x): | |
144 | self.waitingForEngine = False |
|
216 | self.waitingForEngine = False | |
145 | self.didChangeValueForKey_('commandHistory') |
|
217 | self.didChangeValueForKey_('commandHistory') | |
@@ -166,14 +238,14 b' class IPythonCocoaController(NSObject, FrontEndBase):' | |||||
166 | self.didChangeValueForKey_('userNS') |
|
238 | self.didChangeValueForKey_('userNS') | |
167 |
|
239 | |||
168 |
|
240 | |||
169 | def update_cell_prompt(self, result): |
|
241 | def update_cell_prompt(self, result, blockID=None): | |
170 | if(isinstance(result, Failure)): |
|
242 | if(isinstance(result, Failure)): | |
171 | blockID = result.blockID |
|
243 | self.insert_text(self.input_prompt(), | |
|
244 | textRange=NSMakeRange(self.blockRanges[blockID].location,0), | |||
|
245 | scrollToVisible=False | |||
|
246 | ) | |||
172 | else: |
|
247 | else: | |
173 | blockID = result['blockID'] |
|
248 | self.insert_text(self.input_prompt(number=result['number']), | |
174 |
|
||||
175 |
|
||||
176 | self.insert_text(self.input_prompt(result=result), |
|
|||
177 | textRange=NSMakeRange(self.blockRanges[blockID].location,0), |
|
249 | textRange=NSMakeRange(self.blockRanges[blockID].location,0), | |
178 | scrollToVisible=False |
|
250 | scrollToVisible=False | |
179 | ) |
|
251 | ) | |
@@ -188,7 +260,7 b' class IPythonCocoaController(NSObject, FrontEndBase):' | |||||
188 |
|
260 | |||
189 | #print inputRange,self.current_block_range() |
|
261 | #print inputRange,self.current_block_range() | |
190 | self.insert_text('\n' + |
|
262 | self.insert_text('\n' + | |
191 | self.output_prompt(result) + |
|
263 | self.output_prompt(number=result['number']) + | |
192 | result.get('display',{}).get('pprint','') + |
|
264 | result.get('display',{}).get('pprint','') + | |
193 | '\n\n', |
|
265 | '\n\n', | |
194 | textRange=NSMakeRange(inputRange.location+inputRange.length, |
|
266 | textRange=NSMakeRange(inputRange.location+inputRange.length, | |
@@ -197,7 +269,11 b' class IPythonCocoaController(NSObject, FrontEndBase):' | |||||
197 |
|
269 | |||
198 |
|
270 | |||
199 | def render_error(self, failure): |
|
271 | def render_error(self, failure): | |
200 |
self.insert_text('\n |
|
272 | self.insert_text('\n' + | |
|
273 | self.output_prompt() + | |||
|
274 | '\n' + | |||
|
275 | failure.getErrorMessage() + | |||
|
276 | '\n\n') | |||
201 | self.start_new_block() |
|
277 | self.start_new_block() | |
202 | return failure |
|
278 | return failure | |
203 |
|
279 | |||
@@ -288,9 +364,13 b' class IPythonCocoaController(NSObject, FrontEndBase):' | |||||
288 | def current_indent_string(self): |
|
364 | def current_indent_string(self): | |
289 | """returns string for indent or None if no indent""" |
|
365 | """returns string for indent or None if no indent""" | |
290 |
|
366 | |||
291 |
|
|
367 | return self._indent_for_block(self.current_block()) | |
292 | lines = self.current_block().split('\n') |
|
368 | ||
293 | currentIndent = len(lines[-1]) - len(lines[-1]) |
|
369 | ||
|
370 | def _indent_for_block(self, block): | |||
|
371 | lines = block.split('\n') | |||
|
372 | if(len(lines) > 1): | |||
|
373 | currentIndent = len(lines[-1]) - len(lines[-1].lstrip()) | |||
294 | if(currentIndent == 0): |
|
374 | if(currentIndent == 0): | |
295 | currentIndent = self.tabSpaces |
|
375 | currentIndent = self.tabSpaces | |
296 |
|
376 |
@@ -70,3 +70,22 b' class TestIPythonCocoaControler(DeferredTestCase):' | |||||
70 |
|
70 | |||
71 | self.controller.execute(code).addCallback(testCompletes) |
|
71 | self.controller.execute(code).addCallback(testCompletes) | |
72 |
|
72 | |||
|
73 | ||||
|
74 | def testCurrentIndent(self): | |||
|
75 | """test that current_indent_string returns current indent or None. | |||
|
76 | Uses _indent_for_block for direct unit testing. | |||
|
77 | """ | |||
|
78 | ||||
|
79 | self.controller.tabUsesSpaces = True | |||
|
80 | self.assert_(self.controller._indent_for_block("""a=3""") == None) | |||
|
81 | self.assert_(self.controller._indent_for_block("") == None) | |||
|
82 | block = """def test():\n a=3""" | |||
|
83 | self.assert_(self.controller._indent_for_block(block) == \ | |||
|
84 | ' ' * self.controller.tabSpaces) | |||
|
85 | ||||
|
86 | block = """if(True):\n%sif(False):\n%spass""" % \ | |||
|
87 | (' '*self.controller.tabSpaces, | |||
|
88 | 2*' '*self.controller.tabSpaces) | |||
|
89 | self.assert_(self.controller._indent_for_block(block) == \ | |||
|
90 | 2*(' '*self.controller.tabSpaces)) | |||
|
91 |
@@ -24,13 +24,24 b' import string' | |||||
24 | import uuid |
|
24 | import uuid | |
25 | import _ast |
|
25 | import _ast | |
26 |
|
26 | |||
27 | import zope.interface as zi |
|
27 | try: | |
|
28 | from zope.interface import Interface, Attribute, implements, classProvides | |||
|
29 | except ImportError: | |||
|
30 | #zope.interface is not available | |||
|
31 | Interface = object | |||
|
32 | def Attribute(name, doc): pass | |||
|
33 | def implements(interface): pass | |||
|
34 | def classProvides(interface): pass | |||
28 |
|
35 | |||
29 | from IPython.kernel.core.history import FrontEndHistory |
|
36 | from IPython.kernel.core.history import FrontEndHistory | |
30 | from IPython.kernel.core.util import Bunch |
|
37 | from IPython.kernel.core.util import Bunch | |
31 | from IPython.kernel.engineservice import IEngineCore |
|
38 | from IPython.kernel.engineservice import IEngineCore | |
32 |
|
39 | |||
33 | from twisted.python.failure import Failure |
|
40 | try: | |
|
41 | from twisted.python.failure import Failure | |||
|
42 | except ImportError: | |||
|
43 | #Twisted not available | |||
|
44 | Failure = Exception | |||
34 |
|
45 | |||
35 | ############################################################################## |
|
46 | ############################################################################## | |
36 | # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or |
|
47 | # TEMPORARY!!! fake configuration, while we decide whether to use tconfig or | |
@@ -43,7 +54,7 b" rc.prompt_out = r'Out [$number]: '" | |||||
43 |
|
54 | |||
44 | ############################################################################## |
|
55 | ############################################################################## | |
45 |
|
56 | |||
46 |
class IFrontEndFactory( |
|
57 | class IFrontEndFactory(Interface): | |
47 | """Factory interface for frontends.""" |
|
58 | """Factory interface for frontends.""" | |
48 |
|
59 | |||
49 | def __call__(engine=None, history=None): |
|
60 | def __call__(engine=None, history=None): | |
@@ -56,33 +67,30 b' class IFrontEndFactory(zi.Interface):' | |||||
56 |
|
67 | |||
57 |
|
68 | |||
58 |
|
69 | |||
59 |
class IFrontEnd( |
|
70 | class IFrontEnd(Interface): | |
60 | """Interface for frontends. All methods return t.i.d.Deferred""" |
|
71 | """Interface for frontends. All methods return t.i.d.Deferred""" | |
61 |
|
72 | |||
62 |
|
|
73 | Attribute("input_prompt_template", "string.Template instance\ | |
63 | substituteable with execute result.") |
|
74 | substituteable with execute result.") | |
64 |
|
|
75 | Attribute("output_prompt_template", "string.Template instance\ | |
65 | substituteable with execute result.") |
|
76 | substituteable with execute result.") | |
66 |
|
|
77 | Attribute("continuation_prompt_template", "string.Template instance\ | |
67 | substituteable with execute result.") |
|
78 | substituteable with execute result.") | |
68 |
|
79 | |||
69 |
def update_cell_prompt( |
|
80 | def update_cell_prompt(result, blockID=None): | |
70 | """Subclass may override to update the input prompt for a block. |
|
81 | """Subclass may override to update the input prompt for a block. | |
71 | Since this method will be called as a |
|
82 | Since this method will be called as a | |
72 | twisted.internet.defer.Deferred's callback, |
|
83 | twisted.internet.defer.Deferred's callback/errback, | |
73 | implementations should return result when finished. |
|
84 | implementations should return result when finished. | |
74 |
|
85 | |||
75 | NB: result is a failure if the execute returned a failre. |
|
86 | Result is a result dict in case of success, and a | |
76 | To get the blockID, you should do something like:: |
|
87 | twisted.python.util.failure.Failure in case of an error | |
77 | if(isinstance(result, twisted.python.failure.Failure)): |
|
|||
78 | blockID = result.blockID |
|
|||
79 | else: |
|
|||
80 | blockID = result['blockID'] |
|
|||
81 | """ |
|
88 | """ | |
82 |
|
89 | |||
83 | pass |
|
90 | pass | |
84 |
|
91 | |||
85 | def render_result(self, result): |
|
92 | ||
|
93 | def render_result(result): | |||
86 | """Render the result of an execute call. Implementors may choose the |
|
94 | """Render the result of an execute call. Implementors may choose the | |
87 | method of rendering. |
|
95 | method of rendering. | |
88 | For example, a notebook-style frontend might render a Chaco plot |
|
96 | For example, a notebook-style frontend might render a Chaco plot | |
@@ -90,6 +98,7 b' class IFrontEnd(zi.Interface):' | |||||
90 |
|
98 | |||
91 | Parameters: |
|
99 | Parameters: | |
92 | result : dict (result of IEngineBase.execute ) |
|
100 | result : dict (result of IEngineBase.execute ) | |
|
101 | blockID = result['blockID'] | |||
93 |
|
102 | |||
94 | Result: |
|
103 | Result: | |
95 | Output of frontend rendering |
|
104 | Output of frontend rendering | |
@@ -97,22 +106,24 b' class IFrontEnd(zi.Interface):' | |||||
97 |
|
106 | |||
98 | pass |
|
107 | pass | |
99 |
|
108 | |||
100 |
def render_error( |
|
109 | def render_error(failure): | |
101 | """Subclasses must override to render the failure. Since this method |
|
110 | """Subclasses must override to render the failure. Since this method | |
102 | ill be called as a twisted.internet.defer.Deferred's callback, |
|
111 | will be called as a twisted.internet.defer.Deferred's callback, | |
103 | implementations should return result when finished. |
|
112 | implementations should return result when finished. | |
|
113 | ||||
|
114 | blockID = failure.blockID | |||
104 | """ |
|
115 | """ | |
105 |
|
116 | |||
106 | pass |
|
117 | pass | |
107 |
|
118 | |||
108 |
|
119 | |||
109 |
def input_prompt( |
|
120 | def input_prompt(number=''): | |
110 | """Returns the input prompt by subsituting into |
|
121 | """Returns the input prompt by subsituting into | |
111 | self.input_prompt_template |
|
122 | self.input_prompt_template | |
112 | """ |
|
123 | """ | |
113 | pass |
|
124 | pass | |
114 |
|
125 | |||
115 |
def output_prompt( |
|
126 | def output_prompt(number=''): | |
116 | """Returns the output prompt by subsituting into |
|
127 | """Returns the output prompt by subsituting into | |
117 | self.output_prompt_template |
|
128 | self.output_prompt_template | |
118 | """ |
|
129 | """ | |
@@ -159,9 +170,6 b' class FrontEndBase(object):' | |||||
159 | - How do we handle completions? |
|
170 | - How do we handle completions? | |
160 | """ |
|
171 | """ | |
161 |
|
172 | |||
162 | zi.implements(IFrontEnd) |
|
|||
163 | zi.classProvides(IFrontEndFactory) |
|
|||
164 |
|
||||
165 | history_cursor = 0 |
|
173 | history_cursor = 0 | |
166 |
|
174 | |||
167 | current_indent_level = 0 |
|
175 | current_indent_level = 0 | |
@@ -171,24 +179,20 b' class FrontEndBase(object):' | |||||
171 | output_prompt_template = string.Template(rc.prompt_out) |
|
179 | output_prompt_template = string.Template(rc.prompt_out) | |
172 | continuation_prompt_template = string.Template(rc.prompt_in2) |
|
180 | continuation_prompt_template = string.Template(rc.prompt_in2) | |
173 |
|
181 | |||
174 |
def __init__(self, |
|
182 | def __init__(self, shell=None, history=None): | |
175 | assert(engine==None or IEngineCore.providedBy(engine)) |
|
183 | self.shell = shell | |
176 | self.engine = IEngineCore(engine) |
|
|||
177 | if history is None: |
|
184 | if history is None: | |
178 | self.history = FrontEndHistory(input_cache=['']) |
|
185 | self.history = FrontEndHistory(input_cache=['']) | |
179 | else: |
|
186 | else: | |
180 | self.history = history |
|
187 | self.history = history | |
181 |
|
188 | |||
182 |
|
189 | |||
183 |
def input_prompt(self, |
|
190 | def input_prompt(self, number=''): | |
184 | """Returns the current input prompt |
|
191 | """Returns the current input prompt | |
185 |
|
192 | |||
186 | It would be great to use ipython1.core.prompts.Prompt1 here |
|
193 | It would be great to use ipython1.core.prompts.Prompt1 here | |
187 | """ |
|
194 | """ | |
188 |
|
195 | return self.input_prompt_template.safe_substitute({'number':number}) | ||
189 | result.setdefault('number','') |
|
|||
190 |
|
||||
191 | return self.input_prompt_template.safe_substitute(result) |
|
|||
192 |
|
196 | |||
193 |
|
197 | |||
194 | def continuation_prompt(self): |
|
198 | def continuation_prompt(self): | |
@@ -196,10 +200,10 b' class FrontEndBase(object):' | |||||
196 |
|
200 | |||
197 | return self.continuation_prompt_template.safe_substitute() |
|
201 | return self.continuation_prompt_template.safe_substitute() | |
198 |
|
202 | |||
199 |
def output_prompt(self, |
|
203 | def output_prompt(self, number=''): | |
200 | """Returns the output prompt for result""" |
|
204 | """Returns the output prompt for result""" | |
201 |
|
205 | |||
202 |
return self.output_prompt_template.safe_substitute( |
|
206 | return self.output_prompt_template.safe_substitute({'number':number}) | |
203 |
|
207 | |||
204 |
|
208 | |||
205 | def is_complete(self, block): |
|
209 | def is_complete(self, block): | |
@@ -239,7 +243,7 b' class FrontEndBase(object):' | |||||
239 |
|
243 | |||
240 |
|
244 | |||
241 | def execute(self, block, blockID=None): |
|
245 | def execute(self, block, blockID=None): | |
242 | """Execute the block and return result. |
|
246 | """Execute the block and return the result. | |
243 |
|
247 | |||
244 | Parameters: |
|
248 | Parameters: | |
245 | block : {str, AST} |
|
249 | block : {str, AST} | |
@@ -252,32 +256,41 b' class FrontEndBase(object):' | |||||
252 | """ |
|
256 | """ | |
253 |
|
257 | |||
254 | if(not self.is_complete(block)): |
|
258 | if(not self.is_complete(block)): | |
255 |
re |
|
259 | raise Exception("Block is not compilable") | |
256 |
|
260 | |||
257 | if(blockID == None): |
|
261 | if(blockID == None): | |
258 | blockID = uuid.uuid4() #random UUID |
|
262 | blockID = uuid.uuid4() #random UUID | |
259 |
|
263 | |||
260 | d = self.engine.execute(block) |
|
264 | try: | |
261 | d.addCallback(self._add_history, block=block) |
|
265 | result = self.shell.execute(block) | |
262 | d.addBoth(self._add_block_id, blockID) |
|
266 | except Exception,e: | |
263 | d.addBoth(self.update_cell_prompt) |
|
267 | e = self._add_block_id_for_failure(e, blockID=blockID) | |
264 | d.addCallbacks(self.render_result, errback=self.render_error) |
|
268 | e = self.update_cell_prompt(e, blockID=blockID) | |
|
269 | e = self.render_error(e) | |||
|
270 | else: | |||
|
271 | result = self._add_block_id_for_result(result, blockID=blockID) | |||
|
272 | result = self.update_cell_prompt(result, blockID=blockID) | |||
|
273 | result = self.render_result(result) | |||
265 |
|
274 | |||
266 |
return |
|
275 | return result | |
267 |
|
276 | |||
268 |
|
277 | |||
269 | def _add_block_id(self, result, blockID): |
|
278 | def _add_block_id_for_result(self, result, blockID): | |
270 | """Add the blockID to result or failure. Unfortunatley, we have to |
|
279 | """Add the blockID to result or failure. Unfortunatley, we have to | |
271 | treat failures differently than result dicts. |
|
280 | treat failures differently than result dicts. | |
272 | """ |
|
281 | """ | |
273 |
|
282 | |||
274 | if(isinstance(result, Failure)): |
|
283 | result['blockID'] = blockID | |
275 | result.blockID = blockID |
|
|||
276 | else: |
|
|||
277 | result['blockID'] = blockID |
|
|||
278 |
|
284 | |||
279 | return result |
|
285 | return result | |
280 |
|
286 | |||
|
287 | def _add_block_id_for_failure(self, failure, blockID): | |||
|
288 | """_add_block_id_for_failure""" | |||
|
289 | ||||
|
290 | failure.blockID = blockID | |||
|
291 | return failure | |||
|
292 | ||||
|
293 | ||||
281 | def _add_history(self, result, block=None): |
|
294 | def _add_history(self, result, block=None): | |
282 | """Add block to the history""" |
|
295 | """Add block to the history""" | |
283 |
|
296 | |||
@@ -313,20 +326,11 b' class FrontEndBase(object):' | |||||
313 | # Subclasses probably want to override these methods... |
|
326 | # Subclasses probably want to override these methods... | |
314 | ### |
|
327 | ### | |
315 |
|
328 | |||
316 | def update_cell_prompt(self, result): |
|
329 | def update_cell_prompt(self, result, blockID=None): | |
317 | """Subclass may override to update the input prompt for a block. |
|
330 | """Subclass may override to update the input prompt for a block. | |
318 | Since this method will be called as a |
|
331 | Since this method will be called as a | |
319 | twisted.internet.defer.Deferred's callback, implementations should |
|
332 | twisted.internet.defer.Deferred's callback, implementations should | |
320 | return result when finished. |
|
333 | return result when finished. | |
321 |
|
||||
322 | NB: result is a failure if the execute returned a failre. |
|
|||
323 | To get the blockID, you should do something like:: |
|
|||
324 | if(isinstance(result, twisted.python.failure.Failure)): |
|
|||
325 | blockID = result.blockID |
|
|||
326 | else: |
|
|||
327 | blockID = result['blockID'] |
|
|||
328 |
|
||||
329 |
|
||||
330 | """ |
|
334 | """ | |
331 |
|
335 | |||
332 | return result |
|
336 | return result | |
@@ -344,9 +348,60 b' class FrontEndBase(object):' | |||||
344 | def render_error(self, failure): |
|
348 | def render_error(self, failure): | |
345 | """Subclasses must override to render the failure. Since this method |
|
349 | """Subclasses must override to render the failure. Since this method | |
346 | will be called as a twisted.internet.defer.Deferred's callback, |
|
350 | will be called as a twisted.internet.defer.Deferred's callback, | |
347 |
implementations should return result when finished. |
|
351 | implementations should return result when finished. | |
|
352 | """ | |||
348 |
|
353 | |||
349 | return failure |
|
354 | return failure | |
350 |
|
355 | |||
351 |
|
356 | |||
352 |
|
357 | |||
|
358 | class AsyncFrontEndBase(FrontEndBase): | |||
|
359 | """ | |||
|
360 | Overrides FrontEndBase to wrap execute in a deferred result. | |||
|
361 | All callbacks are made as callbacks on the deferred result. | |||
|
362 | """ | |||
|
363 | ||||
|
364 | implements(IFrontEnd) | |||
|
365 | classProvides(IFrontEndFactory) | |||
|
366 | ||||
|
367 | def __init__(self, engine=None, history=None): | |||
|
368 | assert(engine==None or IEngineCore.providedBy(engine)) | |||
|
369 | self.engine = IEngineCore(engine) | |||
|
370 | if history is None: | |||
|
371 | self.history = FrontEndHistory(input_cache=['']) | |||
|
372 | else: | |||
|
373 | self.history = history | |||
|
374 | ||||
|
375 | ||||
|
376 | def execute(self, block, blockID=None): | |||
|
377 | """Execute the block and return the deferred result. | |||
|
378 | ||||
|
379 | Parameters: | |||
|
380 | block : {str, AST} | |||
|
381 | blockID : any | |||
|
382 | Caller may provide an ID to identify this block. | |||
|
383 | result['blockID'] := blockID | |||
|
384 | ||||
|
385 | Result: | |||
|
386 | Deferred result of self.interpreter.execute | |||
|
387 | """ | |||
|
388 | ||||
|
389 | if(not self.is_complete(block)): | |||
|
390 | return Failure(Exception("Block is not compilable")) | |||
|
391 | ||||
|
392 | if(blockID == None): | |||
|
393 | blockID = uuid.uuid4() #random UUID | |||
|
394 | ||||
|
395 | d = self.engine.execute(block) | |||
|
396 | d.addCallback(self._add_history, block=block) | |||
|
397 | d.addCallbacks(self._add_block_id_for_result, | |||
|
398 | errback=self._add_block_id_for_failure, | |||
|
399 | callbackArgs=(blockID,), | |||
|
400 | errbackArgs=(blockID,)) | |||
|
401 | d.addBoth(self.update_cell_prompt, blockID=blockID) | |||
|
402 | d.addCallbacks(self.render_result, | |||
|
403 | errback=self.render_error) | |||
|
404 | ||||
|
405 | return d | |||
|
406 | ||||
|
407 |
@@ -19,7 +19,7 b' import unittest' | |||||
19 | from IPython.frontend import frontendbase |
|
19 | from IPython.frontend import frontendbase | |
20 | from IPython.kernel.engineservice import EngineService |
|
20 | from IPython.kernel.engineservice import EngineService | |
21 |
|
21 | |||
22 | class FrontEndCallbackChecker(frontendbase.FrontEndBase): |
|
22 | class FrontEndCallbackChecker(frontendbase.AsyncFrontEndBase): | |
23 | """FrontEndBase subclass for checking callbacks""" |
|
23 | """FrontEndBase subclass for checking callbacks""" | |
24 | def __init__(self, engine=None, history=None): |
|
24 | def __init__(self, engine=None, history=None): | |
25 | super(FrontEndCallbackChecker, self).__init__(engine=engine, |
|
25 | super(FrontEndCallbackChecker, self).__init__(engine=engine, | |
@@ -28,7 +28,7 b' class FrontEndCallbackChecker(frontendbase.FrontEndBase):' | |||||
28 | self.renderResultCalled = False |
|
28 | self.renderResultCalled = False | |
29 | self.renderErrorCalled = False |
|
29 | self.renderErrorCalled = False | |
30 |
|
30 | |||
31 | def update_cell_prompt(self, result): |
|
31 | def update_cell_prompt(self, result, blockID=None): | |
32 | self.updateCalled = True |
|
32 | self.updateCalled = True | |
33 | return result |
|
33 | return result | |
34 |
|
34 | |||
@@ -44,7 +44,7 b' class FrontEndCallbackChecker(frontendbase.FrontEndBase):' | |||||
44 |
|
44 | |||
45 |
|
45 | |||
46 |
|
46 | |||
47 | class TestFrontendBase(unittest.TestCase): |
|
47 | class TestAsyncFrontendBase(unittest.TestCase): | |
48 | def setUp(self): |
|
48 | def setUp(self): | |
49 | """Setup the EngineService and FrontEndBase""" |
|
49 | """Setup the EngineService and FrontEndBase""" | |
50 |
|
50 | |||
@@ -53,7 +53,7 b' class TestFrontendBase(unittest.TestCase):' | |||||
53 |
|
53 | |||
54 | def test_implements_IFrontEnd(self): |
|
54 | def test_implements_IFrontEnd(self): | |
55 | assert(frontendbase.IFrontEnd.implementedBy( |
|
55 | assert(frontendbase.IFrontEnd.implementedBy( | |
56 |
|
|
56 | frontendbase.AsyncFrontEndBase)) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | def test_is_complete_returns_False_for_incomplete_block(self): |
|
59 | def test_is_complete_returns_False_for_incomplete_block(self): |
@@ -1137,14 +1137,41 b' class SList(list):' | |||||
1137 | res.append(" ".join(lineparts)) |
|
1137 | res.append(" ".join(lineparts)) | |
1138 |
|
1138 | |||
1139 | return res |
|
1139 | return res | |
1140 |
|
1140 | def sort(self,field= None, nums = False): | ||
1141 |
|
1141 | """ sort by specified fields (see fields()) | ||
1142 |
|
|
1142 | ||
|
1143 | Example:: | |||
|
1144 | a.sort(1, nums = True) | |||
|
1145 | ||||
|
1146 | Sorts a by second field, in numerical order (so that 21 > 3) | |||
|
1147 | ||||
|
1148 | """ | |||
1143 |
|
1149 | |||
|
1150 | #decorate, sort, undecorate | |||
|
1151 | if field is not None: | |||
|
1152 | dsu = [[SList([line]).fields(field), line] for line in self] | |||
|
1153 | else: | |||
|
1154 | dsu = [[line, line] for line in self] | |||
|
1155 | if nums: | |||
|
1156 | for i in range(len(dsu)): | |||
|
1157 | numstr = "".join([ch for ch in dsu[i][0] if ch.isdigit()]) | |||
|
1158 | try: | |||
|
1159 | n = int(numstr) | |||
|
1160 | except ValueError: | |||
|
1161 | n = 0; | |||
|
1162 | dsu[i][0] = n | |||
|
1163 | ||||
|
1164 | ||||
|
1165 | dsu.sort() | |||
|
1166 | return SList([t[1] for t in dsu]) | |||
1144 |
|
1167 | |||
1145 | def print_slist(arg): |
|
1168 | def print_slist(arg): | |
1146 | """ Prettier (non-repr-like) and more informative printer for SList """ |
|
1169 | """ Prettier (non-repr-like) and more informative printer for SList """ | |
1147 |
print "SList (.p, .n, .l, .s, .grep(), .fields() available) |
|
1170 | print "SList (.p, .n, .l, .s, .grep(), .fields(), sort() available):" | |
|
1171 | if hasattr(arg, 'hideonce') and arg.hideonce: | |||
|
1172 | arg.hideonce = False | |||
|
1173 | return | |||
|
1174 | ||||
1148 | nlprint(arg) |
|
1175 | nlprint(arg) | |
1149 |
|
1176 | |||
1150 | print_slist = result_display.when_type(SList)(print_slist) |
|
1177 | print_slist = result_display.when_type(SList)(print_slist) |
@@ -168,8 +168,7 b' def startMsg(control_host,control_port=10105):' | |||||
168 | print 'For interactive use, you can make a MultiEngineClient with:' |
|
168 | print 'For interactive use, you can make a MultiEngineClient with:' | |
169 |
|
169 | |||
170 | print 'from IPython.kernel import client' |
|
170 | print 'from IPython.kernel import client' | |
171 |
print "mec = client.MultiEngineClient( |
|
171 | print "mec = client.MultiEngineClient()" | |
172 | (control_host,control_port) |
|
|||
173 |
|
172 | |||
174 | print 'You can then cleanly stop the cluster from IPython using:' |
|
173 | print 'You can then cleanly stop the cluster from IPython using:' | |
175 |
|
174 | |||
@@ -191,16 +190,18 b' def clusterLocal(opt,arg):' | |||||
191 | logfile = pjoin(logdir_base,'ipcluster-') |
|
190 | logfile = pjoin(logdir_base,'ipcluster-') | |
192 |
|
191 | |||
193 | print 'Starting controller:', |
|
192 | print 'Starting controller:', | |
194 | controller = Popen(['ipcontroller','--logfile',logfile]) |
|
193 | controller = Popen(['ipcontroller','--logfile',logfile,'-x','-y']) | |
195 | print 'Controller PID:',controller.pid |
|
194 | print 'Controller PID:',controller.pid | |
196 |
|
195 | |||
197 | print 'Starting engines: ', |
|
196 | print 'Starting engines: ', | |
198 |
time.sleep( |
|
197 | time.sleep(5) | |
199 |
|
198 | |||
200 | englogfile = '%s%s-' % (logfile,controller.pid) |
|
199 | englogfile = '%s%s-' % (logfile,controller.pid) | |
201 | mpi = opt.mpi |
|
200 | mpi = opt.mpi | |
202 | if mpi: # start with mpi - killing the engines with sigterm will not work if you do this |
|
201 | if mpi: # start with mpi - killing the engines with sigterm will not work if you do this | |
203 |
engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi', |
|
202 | engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi', | |
|
203 | mpi, '--logfile',englogfile])] | |||
|
204 | # engines = [Popen(['mpirun', '-np', str(opt.n), 'ipengine', '--mpi', mpi])] | |||
204 | else: # do what we would normally do |
|
205 | else: # do what we would normally do | |
205 | engines = [ Popen(['ipengine','--logfile',englogfile]) |
|
206 | engines = [ Popen(['ipengine','--logfile',englogfile]) | |
206 | for i in range(opt.n) ] |
|
207 | for i in range(opt.n) ] |
@@ -58,12 +58,14 b' def start_engine():' | |||||
58 | kernel_config = kernel_config_manager.get_config_obj() |
|
58 | kernel_config = kernel_config_manager.get_config_obj() | |
59 | core_config = core_config_manager.get_config_obj() |
|
59 | core_config = core_config_manager.get_config_obj() | |
60 |
|
60 | |||
|
61 | ||||
61 | # Execute the mpi import statement that needs to call MPI_Init |
|
62 | # Execute the mpi import statement that needs to call MPI_Init | |
|
63 | global mpi | |||
62 | mpikey = kernel_config['mpi']['default'] |
|
64 | mpikey = kernel_config['mpi']['default'] | |
63 | mpi_import_statement = kernel_config['mpi'].get(mpikey, None) |
|
65 | mpi_import_statement = kernel_config['mpi'].get(mpikey, None) | |
64 | if mpi_import_statement is not None: |
|
66 | if mpi_import_statement is not None: | |
65 | try: |
|
67 | try: | |
66 |
exec mpi_import_statement in |
|
68 | exec mpi_import_statement in globals() | |
67 | except: |
|
69 | except: | |
68 | mpi = None |
|
70 | mpi = None | |
69 | else: |
|
71 | else: |
@@ -3,13 +3,8 b'' | |||||
3 |
|
3 | |||
4 | Importing this module should give you the implementations that are correct |
|
4 | Importing this module should give you the implementations that are correct | |
5 | for your operation system, from platutils_PLATFORMNAME module. |
|
5 | for your operation system, from platutils_PLATFORMNAME module. | |
6 |
|
||||
7 | $Id: ipstruct.py 1005 2006-01-12 08:39:26Z fperez $ |
|
|||
8 |
|
||||
9 |
|
||||
10 | """ |
|
6 | """ | |
11 |
|
7 | |||
12 |
|
||||
13 | #***************************************************************************** |
|
8 | #***************************************************************************** | |
14 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
15 | # |
|
10 | # | |
@@ -21,15 +16,32 b' from IPython import Release' | |||||
21 | __author__ = '%s <%s>' % Release.authors['Ville'] |
|
16 | __author__ = '%s <%s>' % Release.authors['Ville'] | |
22 | __license__ = Release.license |
|
17 | __license__ = Release.license | |
23 |
|
18 | |||
24 |
import os |
|
19 | import os | |
|
20 | import sys | |||
25 |
|
21 | |||
|
22 | # Import the platform-specific implementations | |||
26 | if os.name == 'posix': |
|
23 | if os.name == 'posix': | |
27 |
|
|
24 | import platutils_posix as _platutils | |
28 | elif sys.platform == 'win32': |
|
25 | elif sys.platform == 'win32': | |
29 |
|
|
26 | import platutils_win32 as _platutils | |
30 | else: |
|
27 | else: | |
31 |
|
|
28 | import platutils_dummy as _platutils | |
32 | import warnings |
|
29 | import warnings | |
33 | warnings.warn("Platutils not available for platform '%s', some features may be missing" % |
|
30 | warnings.warn("Platutils not available for platform '%s', some features may be missing" % | |
34 | os.name) |
|
31 | os.name) | |
35 | del warnings |
|
32 | del warnings | |
|
33 | ||||
|
34 | ||||
|
35 | # Functionality that's logically common to all platforms goes here, each | |||
|
36 | # platform-specific module only provides the bits that are OS-dependent. | |||
|
37 | ||||
|
38 | def freeze_term_title(): | |||
|
39 | _platutils.ignore_termtitle = True | |||
|
40 | ||||
|
41 | ||||
|
42 | def set_term_title(title): | |||
|
43 | """Set terminal title using the necessary platform-dependent calls.""" | |||
|
44 | ||||
|
45 | if _platutils.ignore_termtitle: | |||
|
46 | return | |||
|
47 | _platutils.set_term_title(title) |
@@ -3,13 +3,8 b'' | |||||
3 |
|
3 | |||
4 | This has empty implementation of the platutils functions, used for |
|
4 | This has empty implementation of the platutils functions, used for | |
5 | unsupported operating systems. |
|
5 | unsupported operating systems. | |
6 |
|
||||
7 | $Id: ipstruct.py 1005 2006-01-12 08:39:26Z fperez $ |
|
|||
8 |
|
||||
9 |
|
||||
10 | """ |
|
6 | """ | |
11 |
|
7 | |||
12 |
|
||||
13 | #***************************************************************************** |
|
8 | #***************************************************************************** | |
14 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
15 | # |
|
10 | # | |
@@ -21,9 +16,9 b' from IPython import Release' | |||||
21 | __author__ = '%s <%s>' % Release.authors['Ville'] |
|
16 | __author__ = '%s <%s>' % Release.authors['Ville'] | |
22 | __license__ = Release.license |
|
17 | __license__ = Release.license | |
23 |
|
18 | |||
|
19 | # This variable is part of the expected API of the module: | |||
|
20 | ignore_termtitle = True | |||
24 |
|
21 | |||
25 |
def |
|
22 | def set_term_title(*args,**kw): | |
|
23 | """Dummy no-op.""" | |||
26 | pass |
|
24 | pass | |
27 |
|
||||
28 | set_term_title = _dummy |
|
|||
29 |
|
@@ -3,12 +3,8 b'' | |||||
3 |
|
3 | |||
4 | Importing this module directly is not portable - rather, import platutils |
|
4 | Importing this module directly is not portable - rather, import platutils | |
5 | to use these functions in platform agnostic fashion. |
|
5 | to use these functions in platform agnostic fashion. | |
6 |
|
||||
7 | $Id: ipstruct.py 1005 2006-01-12 08:39:26Z fperez $ |
|
|||
8 |
|
||||
9 | """ |
|
6 | """ | |
10 |
|
7 | |||
11 |
|
||||
12 | #***************************************************************************** |
|
8 | #***************************************************************************** | |
13 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
14 | # |
|
10 | # | |
@@ -31,9 +27,6 b' def _dummy_op(*a, **b):' | |||||
31 | def _set_term_title_xterm(title): |
|
27 | def _set_term_title_xterm(title): | |
32 | """ Change virtual terminal title in xterm-workalikes """ |
|
28 | """ Change virtual terminal title in xterm-workalikes """ | |
33 |
|
29 | |||
34 | if ignore_termtitle: |
|
|||
35 | return |
|
|||
36 |
|
||||
37 | sys.stdout.write('\033]%d;%s\007' % (0,title)) |
|
30 | sys.stdout.write('\033]%d;%s\007' % (0,title)) | |
38 |
|
31 | |||
39 |
|
32 | |||
@@ -41,7 +34,3 b" if os.environ.get('TERM','') == 'xterm':" | |||||
41 | set_term_title = _set_term_title_xterm |
|
34 | set_term_title = _set_term_title_xterm | |
42 | else: |
|
35 | else: | |
43 | set_term_title = _dummy_op |
|
36 | set_term_title = _dummy_op | |
44 |
|
||||
45 | def freeze_term_title(): |
|
|||
46 | global ignore_termtitle |
|
|||
47 | ignore_termtitle = True |
|
@@ -3,12 +3,8 b'' | |||||
3 |
|
3 | |||
4 | Importing this module directly is not portable - rather, import platutils |
|
4 | Importing this module directly is not portable - rather, import platutils | |
5 | to use these functions in platform agnostic fashion. |
|
5 | to use these functions in platform agnostic fashion. | |
6 |
|
||||
7 | $Id: ipstruct.py 1005 2006-01-12 08:39:26Z fperez $ |
|
|||
8 |
|
||||
9 | """ |
|
6 | """ | |
10 |
|
7 | |||
11 |
|
||||
12 | #***************************************************************************** |
|
8 | #***************************************************************************** | |
13 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> |
|
9 | # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> | |
14 | # |
|
10 | # | |
@@ -22,35 +18,30 b' __license__ = Release.license' | |||||
22 |
|
18 | |||
23 | import os |
|
19 | import os | |
24 |
|
20 | |||
25 |
ignore_termtitle = |
|
21 | ignore_termtitle = False | |
26 |
|
22 | |||
27 | try: |
|
23 | try: | |
28 | import ctypes |
|
24 | import ctypes | |
29 | SetConsoleTitleW=ctypes.windll.kernel32.SetConsoleTitleW |
|
25 | ||
30 | SetConsoleTitleW.argtypes=[ctypes.c_wchar_p] |
|
26 | SetConsoleTitleW = ctypes.windll.kernel32.SetConsoleTitleW | |
31 | def _set_term_title(title): |
|
27 | SetConsoleTitleW.argtypes = [ctypes.c_wchar_p] | |
32 | """ Set terminal title using the ctypes""" |
|
28 | ||
|
29 | def set_term_title(title): | |||
|
30 | """Set terminal title using ctypes to access the Win32 APIs.""" | |||
33 | SetConsoleTitleW(title) |
|
31 | SetConsoleTitleW(title) | |
34 |
|
32 | |||
35 | except ImportError: |
|
33 | except ImportError: | |
36 |
def |
|
34 | def set_term_title(title): | |
37 |
""" |
|
35 | """Set terminal title using the 'title' command.""" | |
38 | curr=os.getcwd() |
|
36 | global ignore_termtitle | |
39 | os.chdir("C:") #Cannot be on network share when issuing system commands |
|
37 | ||
40 | ret = os.system("title " + title) |
|
38 | try: | |
41 | os.chdir(curr) |
|
39 | # Cannot be on network share when issuing system commands | |
|
40 | curr = os.getcwd() | |||
|
41 | os.chdir("C:") | |||
|
42 | ret = os.system("title " + title) | |||
|
43 | finally: | |||
|
44 | os.chdir(curr) | |||
42 | if ret: |
|
45 | if ret: | |
43 | ignore_termtitle = 1 |
|
46 | # non-zero return code signals error, don't try again | |
44 |
|
47 | ignore_termtitle = True | ||
45 | def set_term_title(title): |
|
|||
46 | """ Set terminal title using the 'title' command """ |
|
|||
47 | global ignore_termtitle |
|
|||
48 |
|
||||
49 | if ignore_termtitle: |
|
|||
50 | return |
|
|||
51 | _set_term_title(title) |
|
|||
52 |
|
||||
53 | def freeze_term_title(): |
|
|||
54 | global ignore_termtitle |
|
|||
55 | ignore_termtitle = 1 |
|
|||
56 |
|
1 | NO CONTENT: file renamed from IPython/testing/ipdoctest.py to IPython/testing/attic/ipdoctest.py |
|
NO CONTENT: file renamed from IPython/testing/ipdoctest.py to IPython/testing/attic/ipdoctest.py |
1 | NO CONTENT: file renamed from IPython/testing/parametric.py to IPython/testing/attic/parametric.py |
|
NO CONTENT: file renamed from IPython/testing/parametric.py to IPython/testing/attic/parametric.py |
1 | NO CONTENT: file renamed from IPython/testing/tcommon.py to IPython/testing/attic/tcommon.py |
|
NO CONTENT: file renamed from IPython/testing/tcommon.py to IPython/testing/attic/tcommon.py |
1 | NO CONTENT: file renamed from IPython/testing/testTEMPLATE.py to IPython/testing/attic/testTEMPLATE.py |
|
NO CONTENT: file renamed from IPython/testing/testTEMPLATE.py to IPython/testing/attic/testTEMPLATE.py |
1 | NO CONTENT: file renamed from IPython/testing/tstTEMPLATE_doctest.py to IPython/testing/attic/tstTEMPLATE_doctest.py |
|
NO CONTENT: file renamed from IPython/testing/tstTEMPLATE_doctest.py to IPython/testing/attic/tstTEMPLATE_doctest.py |
1 | NO CONTENT: file renamed from IPython/testing/tstTEMPLATE_doctest.txt to IPython/testing/attic/tstTEMPLATE_doctest.txt |
|
NO CONTENT: file renamed from IPython/testing/tstTEMPLATE_doctest.txt to IPython/testing/attic/tstTEMPLATE_doctest.txt |
@@ -1,6 +1,20 b'' | |||||
1 | """Utilities for testing code. |
|
1 | """DEPRECATED - use IPython.testing.util instead. | |
|
2 | ||||
|
3 | Utilities for testing code. | |||
2 | """ |
|
4 | """ | |
3 |
|
5 | |||
|
6 | ############################################################################# | |||
|
7 | ||||
|
8 | # This was old testing code we never really used in IPython. The pieces of | |||
|
9 | # testing machinery from snakeoil that were good have already been merged into | |||
|
10 | # the nose plugin, so this can be taken away soon. Leave a warning for now, | |||
|
11 | # we'll remove it in a later release (around 0.10 or so). | |||
|
12 | from warnings import warn | |||
|
13 | warn('This will be removed soon. Use IPython.testing.util instead', | |||
|
14 | DeprecationWarning) | |||
|
15 | ||||
|
16 | ############################################################################# | |||
|
17 | ||||
4 | # Required modules and packages |
|
18 | # Required modules and packages | |
5 |
|
19 | |||
6 | # Standard Python lib |
|
20 | # Standard Python lib | |
@@ -36,7 +50,7 b' def test_path(path):' | |||||
36 |
|
50 | |||
37 | This finds the correct path of the test package on disk, and prepends it |
|
51 | This finds the correct path of the test package on disk, and prepends it | |
38 | to the input path.""" |
|
52 | to the input path.""" | |
39 |
|
53 | |||
40 | return os.path.join(TEST_PATH,path) |
|
54 | return os.path.join(TEST_PATH,path) | |
41 |
|
55 | |||
42 | def fullPath(startPath,files): |
|
56 | def fullPath(startPath,files): | |
@@ -55,7 +69,7 b' def fullPath(startPath,files):' | |||||
55 | One or more files. |
|
69 | One or more files. | |
56 |
|
70 | |||
57 | :Examples: |
|
71 | :Examples: | |
58 |
|
72 | |||
59 | >>> fullPath('/foo/bar.py',['a.txt','b.txt']) |
|
73 | >>> fullPath('/foo/bar.py',['a.txt','b.txt']) | |
60 | ['/foo/a.txt', '/foo/b.txt'] |
|
74 | ['/foo/a.txt', '/foo/b.txt'] | |
61 |
|
75 | |||
@@ -66,7 +80,7 b' def fullPath(startPath,files):' | |||||
66 | >>> fullPath('/foo','a.txt') |
|
80 | >>> fullPath('/foo','a.txt') | |
67 | ['/a.txt'] |
|
81 | ['/a.txt'] | |
68 | """ |
|
82 | """ | |
69 |
|
83 | |||
70 | files = utils.list_strings(files) |
|
84 | files = utils.list_strings(files) | |
71 | base = os.path.split(startPath)[0] |
|
85 | base = os.path.split(startPath)[0] | |
72 | return [ os.path.join(base,f) for f in files ] |
|
86 | return [ os.path.join(base,f) for f in files ] |
@@ -3,40 +3,15 b'' | |||||
3 | ========================================= |
|
3 | ========================================= | |
4 |
|
4 | |||
5 | The way doctest loads these, the entire document is applied as a single test |
|
5 | The way doctest loads these, the entire document is applied as a single test | |
6 |
rather than multiple individual ones, unfortunately |
|
6 | rather than multiple individual ones, unfortunately:: | |
7 |
|
7 | |||
8 |
|
||||
9 | Auto-generated tests |
|
|||
10 | ==================== |
|
|||
11 |
|
||||
12 |
|
||||
13 | ---------------------------------------------------------------------------- |
|
|||
14 |
|
||||
15 | Begin included file tst_tools_utils_doctest2.py:: |
|
|||
16 |
|
||||
17 | # Setup - all imports are done in tcommon |
|
|||
18 | >>> from IPython.testing import tcommon |
|
|||
19 | >>> from IPython.testing.tcommon import * |
|
|||
20 |
|
||||
21 | # Doctest code begins here |
|
|||
22 | >>> from IPython.tools import utils |
|
8 | >>> from IPython.tools import utils | |
23 |
|
9 | |||
24 | # Some other tests for utils |
|
10 | # Some other tests for utils | |
25 |
|
11 | |||
26 | >>> utils.marquee('Testing marquee') |
|
12 | >>> utils.marquee('Testing marquee') | |
27 | '****************************** Testing marquee ******************************' |
|
13 | '****************************** Testing marquee ******************************' | |
28 |
|
14 | |||
29 | >>> utils.marquee('Another test',30,'.') |
|
15 | >>> utils.marquee('Another test',30,'.') | |
30 | '........ Another test ........' |
|
16 | '........ Another test ........' | |
31 |
|
||||
32 |
|
||||
33 | End included file tst_tools_utils_doctest2.py |
|
|||
34 |
|
||||
35 | ---------------------------------------------------------------------------- |
|
|||
36 |
|
||||
37 |
|
||||
38 |
|
||||
39 | Manually generated tests |
|
|||
40 | ======================== |
|
|||
41 |
|
17 | |||
42 | These are one-off tests written by hand, copied from an interactive prompt. |
|
@@ -16,7 +16,6 b' graft IPython/tools' | |||||
16 | graft docs |
|
16 | graft docs | |
17 | exclude docs/\#* |
|
17 | exclude docs/\#* | |
18 | exclude docs/man/*.1 |
|
18 | exclude docs/man/*.1 | |
19 | exclude docs/ChangeLog.* |
|
|||
20 |
|
19 | |||
21 | # There seems to be no way of excluding whole subdirectories, other than |
|
20 | # There seems to be no way of excluding whole subdirectories, other than | |
22 | # manually excluding all their subdirs. distutils really is horrible... |
|
21 | # manually excluding all their subdirs. distutils really is horrible... |
@@ -9,7 +9,7 b'' | |||||
9 | (defconst ipython-version "$Revision: 2927 $" |
|
9 | (defconst ipython-version "$Revision: 2927 $" | |
10 | "VC version number.") |
|
10 | "VC version number.") | |
11 |
|
11 | |||
12 |
;;; Commentary |
|
12 | ;;; Commentary | |
13 | ;; This library makes all the functionality python-mode has when running with |
|
13 | ;; This library makes all the functionality python-mode has when running with | |
14 | ;; the normal python-interpreter available for ipython, too. It also enables a |
|
14 | ;; the normal python-interpreter available for ipython, too. It also enables a | |
15 | ;; persistent py-shell command history across sessions (if you exit python |
|
15 | ;; persistent py-shell command history across sessions (if you exit python | |
@@ -31,13 +31,20 b'' | |||||
31 | ;; To start an interactive ipython session run `py-shell' with ``M-x py-shell`` |
|
31 | ;; To start an interactive ipython session run `py-shell' with ``M-x py-shell`` | |
32 | ;; (or the default keybinding ``C-c C-!``). |
|
32 | ;; (or the default keybinding ``C-c C-!``). | |
33 | ;; |
|
33 | ;; | |
|
34 | ;; You can customize the arguments passed to the IPython instance at startup by | |||
|
35 | ;; setting the ``py-python-command-args`` variable. For example, to start | |||
|
36 | ;; always in ``pylab`` mode with hardcoded light-background colors, you can | |||
|
37 | ;; use:: | |||
|
38 | ;; | |||
|
39 | ;; (setq py-python-command-args '("-pylab" "-colors" "LightBG")) | |||
|
40 | ;; | |||
|
41 | ;; | |||
34 | ;; NOTE: This mode is currently somewhat alpha and although I hope that it |
|
42 | ;; NOTE: This mode is currently somewhat alpha and although I hope that it | |
35 | ;; will work fine for most cases, doing certain things (like the |
|
43 | ;; will work fine for most cases, doing certain things (like the | |
36 | ;; autocompletion and a decent scheme to switch between python interpreters) |
|
44 | ;; autocompletion and a decent scheme to switch between python interpreters) | |
37 | ;; properly will also require changes to ipython that will likely have to wait |
|
45 | ;; properly will also require changes to ipython that will likely have to wait | |
38 | ;; for a larger rewrite scheduled some time in the future. |
|
46 | ;; for a larger rewrite scheduled some time in the future. | |
39 |
;; |
|
47 | ;; | |
40 | ;; Also note that you currently NEED THE CVS VERSION OF PYTHON.EL. |
|
|||
41 | ;; |
|
48 | ;; | |
42 | ;; Further note that I don't know whether this runs under windows or not and |
|
49 | ;; Further note that I don't know whether this runs under windows or not and | |
43 | ;; that if it doesn't I can't really help much, not being afflicted myself. |
|
50 | ;; that if it doesn't I can't really help much, not being afflicted myself. | |
@@ -46,14 +53,15 b'' | |||||
46 | ;; Hints for effective usage |
|
53 | ;; Hints for effective usage | |
47 | ;; ------------------------- |
|
54 | ;; ------------------------- | |
48 | ;; |
|
55 | ;; | |
49 |
;; - IMO the best feature by far of the ipython/emacs combo is how much easier |
|
56 | ;; - IMO the best feature by far of the ipython/emacs combo is how much easier | |
50 |
;; makes it to find and fix bugs thanks to the ``%pdb on |
|
57 | ;; it makes it to find and fix bugs thanks to the ``%pdb on or %debug``/ | |
51 |
;; it: first in the ipython to shell do ``%pdb on`` then |
|
58 | ;; pdbtrack combo. Try it: first in the ipython to shell do ``%pdb on`` then | |
52 |
;; raise an exception (FIXME nice example) |
|
59 | ;; do something that will raise an exception (FIXME nice example), or type | |
53 | ;; inspect the live objects in each stack frames and to jump to the |
|
60 | ;; ``%debug`` after the exception has been raised. YOu'll be amazed at how | |
54 | ;; corresponding sourcecode locations as you walk up and down the stack trace |
|
61 | ;; easy it is to inspect the live objects in each stack frames and to jump to | |
55 | ;; (even without ``%pdb on`` you can always use ``C-c -`` (`py-up-exception') |
|
62 | ;; the corresponding sourcecode locations as you walk up and down the stack | |
56 | ;; to jump to the corresponding source code locations). |
|
63 | ;; trace (even without ``%pdb on`` you can always use ``C-c -`` | |
|
64 | ;; (`py-up-exception') to jump to the corresponding source code locations). | |||
57 | ;; |
|
65 | ;; | |
58 | ;; - emacs gives you much more powerful commandline editing and output searching |
|
66 | ;; - emacs gives you much more powerful commandline editing and output searching | |
59 | ;; capabilities than ipython-standalone -- isearch is your friend if you |
|
67 | ;; capabilities than ipython-standalone -- isearch is your friend if you | |
@@ -65,9 +73,9 b'' | |||||
65 | ;; ipython, you can change ``meta p`` etc. for ``control p``):: |
|
73 | ;; ipython, you can change ``meta p`` etc. for ``control p``):: | |
66 | ;; |
|
74 | ;; | |
67 | ;; (require 'comint) |
|
75 | ;; (require 'comint) | |
68 |
;; (define-key comint-mode-map [(meta p)] |
|
76 | ;; (define-key comint-mode-map [(meta p)] | |
69 | ;; 'comint-previous-matching-input-from-input) |
|
77 | ;; 'comint-previous-matching-input-from-input) | |
70 |
;; (define-key comint-mode-map [(meta n)] |
|
78 | ;; (define-key comint-mode-map [(meta n)] | |
71 | ;; 'comint-next-matching-input-from-input) |
|
79 | ;; 'comint-next-matching-input-from-input) | |
72 | ;; (define-key comint-mode-map [(control meta n)] |
|
80 | ;; (define-key comint-mode-map [(control meta n)] | |
73 | ;; 'comint-next-input) |
|
81 | ;; 'comint-next-input) | |
@@ -79,8 +87,8 b'' | |||||
79 | ;; variables comes later). |
|
87 | ;; variables comes later). | |
80 | ;; |
|
88 | ;; | |
81 | ;; Please send comments and feedback to the ipython-list |
|
89 | ;; Please send comments and feedback to the ipython-list | |
82 |
;; (<ipython-user@scipy. |
|
90 | ;; (<ipython-user@scipy.org>) where I (a.s.) or someone else will try to | |
83 |
;; answer them (it helps if you specify your emacs version, OS etc; |
|
91 | ;; answer them (it helps if you specify your emacs version, OS etc; | |
84 | ;; familiarity with <http://www.catb.org/~esr/faqs/smart-questions.html> might |
|
92 | ;; familiarity with <http://www.catb.org/~esr/faqs/smart-questions.html> might | |
85 | ;; speed up things further). |
|
93 | ;; speed up things further). | |
86 | ;; |
|
94 | ;; | |
@@ -106,10 +114,10 b'' | |||||
106 | ;; - neither:: |
|
114 | ;; - neither:: | |
107 | ;; |
|
115 | ;; | |
108 | ;; (py-shell "-c print 'FOOBAR'") |
|
116 | ;; (py-shell "-c print 'FOOBAR'") | |
109 | ;; |
|
117 | ;; | |
110 | ;; nor:: |
|
118 | ;; nor:: | |
111 | ;; |
|
119 | ;; | |
112 |
;; (let ((py-python-command-args (append py-python-command-args |
|
120 | ;; (let ((py-python-command-args (append py-python-command-args | |
113 | ;; '("-c" "print 'FOOBAR'")))) |
|
121 | ;; '("-c" "print 'FOOBAR'")))) | |
114 | ;; (py-shell)) |
|
122 | ;; (py-shell)) | |
115 | ;; |
|
123 | ;; | |
@@ -127,7 +135,7 b'' | |||||
127 |
|
135 | |||
128 | (defcustom ipython-command "ipython" |
|
136 | (defcustom ipython-command "ipython" | |
129 | "*Shell command used to start ipython." |
|
137 | "*Shell command used to start ipython." | |
130 |
:type 'string |
|
138 | :type 'string | |
131 | :group 'python) |
|
139 | :group 'python) | |
132 |
|
140 | |||
133 | ;; Users can set this to nil |
|
141 | ;; Users can set this to nil | |
@@ -138,7 +146,7 b'' | |||||
138 | (defvar ipython-backup-of-py-python-command nil |
|
146 | (defvar ipython-backup-of-py-python-command nil | |
139 | "HACK") |
|
147 | "HACK") | |
140 |
|
148 | |||
141 |
|
149 | |||
142 | (defvar ipython-de-input-prompt-regexp "\\(?: |
|
150 | (defvar ipython-de-input-prompt-regexp "\\(?: | |
143 | In \\[[0-9]+\\]: *.* |
|
151 | In \\[[0-9]+\\]: *.* | |
144 | ----+> \\(.* |
|
152 | ----+> \\(.* | |
@@ -181,16 +189,16 b' the second for a \'normal\' command, and the third for a multiline command.")' | |||||
181 | ;;XXX this is really just a cheap hack, it only completes symbols in the |
|
189 | ;;XXX this is really just a cheap hack, it only completes symbols in the | |
182 | ;;interactive session -- useful nonetheless. |
|
190 | ;;interactive session -- useful nonetheless. | |
183 | (define-key py-mode-map [(meta tab)] 'ipython-complete) |
|
191 | (define-key py-mode-map [(meta tab)] 'ipython-complete) | |
184 |
|
192 | |||
185 | ) |
|
193 | ) | |
186 | (add-hook 'py-shell-hook 'ipython-shell-hook) |
|
194 | (add-hook 'py-shell-hook 'ipython-shell-hook) | |
187 | ;; Regular expression that describes tracebacks for IPython in context and |
|
195 | ;; Regular expression that describes tracebacks for IPython in context and | |
188 |
;; verbose mode. |
|
196 | ;; verbose mode. | |
189 |
|
197 | |||
190 | ;;Adapt python-mode settings for ipython. |
|
198 | ;;Adapt python-mode settings for ipython. | |
191 | ;; (this works for %xmode 'verbose' or 'context') |
|
199 | ;; (this works for %xmode 'verbose' or 'context') | |
192 |
|
200 | |||
193 |
;; XXX putative regexps for syntax errors; unfortunately the |
|
201 | ;; XXX putative regexps for syntax errors; unfortunately the | |
194 | ;; current python-mode traceback-line-re scheme is too primitive, |
|
202 | ;; current python-mode traceback-line-re scheme is too primitive, | |
195 | ;; so it's either matching syntax errors, *or* everything else |
|
203 | ;; so it's either matching syntax errors, *or* everything else | |
196 | ;; (XXX: should ask Fernando for a change) |
|
204 | ;; (XXX: should ask Fernando for a change) | |
@@ -200,20 +208,20 b' the second for a \'normal\' command, and the third for a multiline command.")' | |||||
200 | (setq py-traceback-line-re |
|
208 | (setq py-traceback-line-re | |
201 | "\\(^[^\t >].+?\\.py\\).*\n +[0-9]+[^\00]*?\n-+> \\([0-9]+\\)+") |
|
209 | "\\(^[^\t >].+?\\.py\\).*\n +[0-9]+[^\00]*?\n-+> \\([0-9]+\\)+") | |
202 |
|
210 | |||
203 |
|
211 | |||
204 | ;; Recognize the ipython pdb, whose prompt is 'ipdb>' or 'ipydb>' |
|
212 | ;; Recognize the ipython pdb, whose prompt is 'ipdb>' or 'ipydb>' | |
205 | ;;instead of '(Pdb)' |
|
213 | ;;instead of '(Pdb)' | |
206 | (setq py-pdbtrack-input-prompt "\n[(<]*[Ii]?[Pp]y?db[>)]+ ") |
|
214 | (setq py-pdbtrack-input-prompt "\n[(<]*[Ii]?[Pp]y?db[>)]+ ") | |
207 | (setq pydb-pydbtrack-input-prompt "\n[(]*ipydb[>)]+ ") |
|
215 | (setq pydb-pydbtrack-input-prompt "\n[(]*ipydb[>)]+ ") | |
208 |
|
216 | |||
209 | (setq py-shell-input-prompt-1-regexp "^In \\[[0-9]+\\]: *" |
|
217 | (setq py-shell-input-prompt-1-regexp "^In \\[[0-9]+\\]: *" | |
210 | py-shell-input-prompt-2-regexp "^ [.][.][.]+: *" ) |
|
218 | py-shell-input-prompt-2-regexp "^ [.][.][.]+: *" ) | |
211 | ;; select a suitable color-scheme |
|
219 | ;; select a suitable color-scheme | |
212 | (unless (member "-colors" py-python-command-args) |
|
220 | (unless (member "-colors" py-python-command-args) | |
213 |
(setq py-python-command-args |
|
221 | (setq py-python-command-args | |
214 |
(nconc py-python-command-args |
|
222 | (nconc py-python-command-args | |
215 | (list "-colors" |
|
223 | (list "-colors" | |
216 |
(cond |
|
224 | (cond | |
217 | ((eq frame-background-mode 'dark) |
|
225 | ((eq frame-background-mode 'dark) | |
218 | "Linux") |
|
226 | "Linux") | |
219 | ((eq frame-background-mode 'light) |
|
227 | ((eq frame-background-mode 'light) | |
@@ -253,7 +261,7 b' buffer already exists."' | |||||
253 |
|
261 | |||
254 | (defadvice py-execute-region (around py-execute-buffer-ensure-process) |
|
262 | (defadvice py-execute-region (around py-execute-buffer-ensure-process) | |
255 | "HACK: if `py-shell' is not active or ASYNC is explicitly desired, fall back |
|
263 | "HACK: if `py-shell' is not active or ASYNC is explicitly desired, fall back | |
256 |
to python instead of ipython." |
|
264 | to python instead of ipython." | |
257 | (let ((py-which-shell (if (and (comint-check-proc "*Python*") (not async)) |
|
265 | (let ((py-which-shell (if (and (comint-check-proc "*Python*") (not async)) | |
258 | py-python-command |
|
266 | py-python-command | |
259 | ipython-backup-of-py-python-command))) |
|
267 | ipython-backup-of-py-python-command))) | |
@@ -267,14 +275,14 b' be used in doctests. Example:' | |||||
267 |
|
275 | |||
268 |
|
276 | |||
269 | In [1]: import sys |
|
277 | In [1]: import sys | |
270 |
|
278 | |||
271 | In [2]: sys.stdout.write 'Hi!\n' |
|
279 | In [2]: sys.stdout.write 'Hi!\n' | |
272 | ------> sys.stdout.write ('Hi!\n') |
|
280 | ------> sys.stdout.write ('Hi!\n') | |
273 | Hi! |
|
281 | Hi! | |
274 |
|
282 | |||
275 | In [3]: 3 + 4 |
|
283 | In [3]: 3 + 4 | |
276 | Out[3]: 7 |
|
284 | Out[3]: 7 | |
277 |
|
285 | |||
278 | gets converted to: |
|
286 | gets converted to: | |
279 |
|
287 | |||
280 | >>> import sys |
|
288 | >>> import sys | |
@@ -288,7 +296,7 b' gets converted to:' | |||||
288 | ;(message (format "###DEBUG s:%de:%d" start end)) |
|
296 | ;(message (format "###DEBUG s:%de:%d" start end)) | |
289 | (save-excursion |
|
297 | (save-excursion | |
290 | (save-match-data |
|
298 | (save-match-data | |
291 |
;; replace ``In [3]: bla`` with ``>>> bla`` and |
|
299 | ;; replace ``In [3]: bla`` with ``>>> bla`` and | |
292 | ;; ``... : bla`` with ``... bla`` |
|
300 | ;; ``... : bla`` with ``... bla`` | |
293 | (goto-char start) |
|
301 | (goto-char start) | |
294 | (while (re-search-forward ipython-de-input-prompt-regexp end t) |
|
302 | (while (re-search-forward ipython-de-input-prompt-regexp end t) | |
@@ -302,7 +310,7 b' gets converted to:' | |||||
302 | (while (re-search-forward ipython-de-output-prompt-regexp end t) |
|
310 | (while (re-search-forward ipython-de-output-prompt-regexp end t) | |
303 | (replace-match "" t nil))))) |
|
311 | (replace-match "" t nil))))) | |
304 |
|
312 | |||
305 |
(defvar ipython-completion-command-string |
|
313 | (defvar ipython-completion-command-string | |
306 | "print ';'.join(__IP.Completer.all_completions('%s')) #PYTHON-MODE SILENT\n" |
|
314 | "print ';'.join(__IP.Completer.all_completions('%s')) #PYTHON-MODE SILENT\n" | |
307 | "The string send to ipython to query for all possible completions") |
|
315 | "The string send to ipython to query for all possible completions") | |
308 |
|
316 | |||
@@ -332,19 +340,19 b' in the current *Python* session."' | |||||
332 | (completion-table nil) |
|
340 | (completion-table nil) | |
333 | completion |
|
341 | completion | |
334 | (comint-output-filter-functions |
|
342 | (comint-output-filter-functions | |
335 |
(append comint-output-filter-functions |
|
343 | (append comint-output-filter-functions | |
336 | '(ansi-color-filter-apply |
|
344 | '(ansi-color-filter-apply | |
337 |
(lambda (string) |
|
345 | (lambda (string) | |
338 | ;(message (format "DEBUG filtering: %s" string)) |
|
346 | ;(message (format "DEBUG filtering: %s" string)) | |
339 | (setq ugly-return (concat ugly-return string)) |
|
347 | (setq ugly-return (concat ugly-return string)) | |
340 |
(delete-region comint-last-output-start |
|
348 | (delete-region comint-last-output-start | |
341 | (process-mark (get-buffer-process (current-buffer))))))))) |
|
349 | (process-mark (get-buffer-process (current-buffer))))))))) | |
342 | ;(message (format "#DEBUG pattern: '%s'" pattern)) |
|
350 | ;(message (format "#DEBUG pattern: '%s'" pattern)) | |
343 |
(process-send-string python-process |
|
351 | (process-send-string python-process | |
344 | (format ipython-completion-command-string pattern)) |
|
352 | (format ipython-completion-command-string pattern)) | |
345 | (accept-process-output python-process) |
|
353 | (accept-process-output python-process) | |
346 | ;(message (format "DEBUG return: %s" ugly-return)) |
|
354 | ;(message (format "DEBUG return: %s" ugly-return)) | |
347 |
(setq completions |
|
355 | (setq completions | |
348 | (split-string (substring ugly-return 0 (position ?\n ugly-return)) sep)) |
|
356 | (split-string (substring ugly-return 0 (position ?\n ugly-return)) sep)) | |
349 | (setq completion-table (loop for str in completions |
|
357 | (setq completion-table (loop for str in completions | |
350 | collect (list str nil))) |
|
358 | collect (list str nil))) | |
@@ -376,22 +384,22 b' in the current *Python* session."' | |||||
376 | ;; to let ipython have the complete line, so that context can be used |
|
384 | ;; to let ipython have the complete line, so that context can be used | |
377 | ;; to do things like filename completion etc. |
|
385 | ;; to do things like filename completion etc. | |
378 | (beg (save-excursion (skip-chars-backward "a-z0-9A-Z_./" (point-at-bol)) |
|
386 | (beg (save-excursion (skip-chars-backward "a-z0-9A-Z_./" (point-at-bol)) | |
379 |
(point))) |
|
387 | (point))) | |
380 | (end (point)) |
|
388 | (end (point)) | |
381 | (pattern (buffer-substring-no-properties beg end)) |
|
389 | (pattern (buffer-substring-no-properties beg end)) | |
382 | (completions nil) |
|
390 | (completions nil) | |
383 | (completion-table nil) |
|
391 | (completion-table nil) | |
384 | completion |
|
392 | completion | |
385 | (comint-preoutput-filter-functions |
|
393 | (comint-preoutput-filter-functions | |
386 |
(append comint-preoutput-filter-functions |
|
394 | (append comint-preoutput-filter-functions | |
387 | '(ansi-color-filter-apply |
|
395 | '(ansi-color-filter-apply | |
388 |
(lambda (string) |
|
396 | (lambda (string) | |
389 | (setq ugly-return (concat ugly-return string)) |
|
397 | (setq ugly-return (concat ugly-return string)) | |
390 | ""))))) |
|
398 | ""))))) | |
391 |
(process-send-string python-process |
|
399 | (process-send-string python-process | |
392 | (format ipython-completion-command-string pattern)) |
|
400 | (format ipython-completion-command-string pattern)) | |
393 | (accept-process-output python-process) |
|
401 | (accept-process-output python-process) | |
394 |
(setq completions |
|
402 | (setq completions | |
395 | (split-string (substring ugly-return 0 (position ?\n ugly-return)) sep)) |
|
403 | (split-string (substring ugly-return 0 (position ?\n ugly-return)) sep)) | |
396 | ;(message (format "DEBUG completions: %S" completions)) |
|
404 | ;(message (format "DEBUG completions: %S" completions)) | |
397 | (setq completion-table (loop for str in completions |
|
405 | (setq completion-table (loop for str in completions |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: modified file |
|
NO CONTENT: modified file | ||
The requested commit or file is too big and content was truncated. Show full diff |
1 | NO CONTENT: file was removed, binary diff hidden |
|
NO CONTENT: file was removed, binary diff hidden |
1 | NO CONTENT: file was removed, binary diff hidden |
|
NO CONTENT: file was removed, binary diff hidden |
1 | NO CONTENT: file was removed, binary diff hidden |
|
NO CONTENT: file was removed, binary diff hidden |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
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