##// END OF EJS Templates
IPython behaves properly when started in a directory with non-ascii characters in the name. + Unit test....
Thomas Kluyver -
Show More
@@ -0,0 +1,33 b''
1 """Tests for IPython.core.application"""
2
3 import os
4 import tempfile
5
6 from IPython.core.application import Application
7
8 def test_unicode_cwd():
9 """Check that IPython can start with unicode characters in the path."""
10 wd = tempfile.mkdtemp(suffix="€")
11
12 old_wd = os.getcwdu()
13 os.chdir(wd)
14 #raise Exception(repr(os.getcwd()))
15 try:
16 app = Application()
17 # The lines below are copied from Application.initialize()
18 app.create_default_config()
19 app.log_default_config()
20 app.set_default_config_log_level()
21
22 # Find resources needed for filesystem access, using information from
23 # the above two
24 app.find_ipython_dir()
25 app.find_resources()
26 app.find_config_file_name()
27 app.find_config_file_paths()
28
29 # File-based config
30 app.pre_load_file_config()
31 app.load_file_config(suppress_errors=False)
32 finally:
33 os.chdir(old_wd)
@@ -1,459 +1,461 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for IPython.
4 4
5 5 All top-level applications should use the classes in this module for
6 6 handling configuration and creating componenets.
7 7
8 8 The job of an :class:`Application` is to create the master configuration
9 9 object and then create the configurable objects, passing the config to them.
10 10
11 11 Authors:
12 12
13 13 * Brian Granger
14 14 * Fernando Perez
15 15
16 16 Notes
17 17 -----
18 18 """
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Copyright (C) 2008-2009 The IPython Development Team
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #-----------------------------------------------------------------------------
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Imports
29 29 #-----------------------------------------------------------------------------
30 30
31 31 import logging
32 32 import os
33 33 import sys
34 34
35 35 from IPython.core import release, crashhandler
36 36 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
37 37 from IPython.config.loader import (
38 38 PyFileConfigLoader,
39 39 ArgParseConfigLoader,
40 40 Config,
41 41 )
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Classes and functions
45 45 #-----------------------------------------------------------------------------
46 46
47 47 class ApplicationError(Exception):
48 48 pass
49 49
50 50
51 51 class BaseAppConfigLoader(ArgParseConfigLoader):
52 52 """Default command line options for IPython based applications."""
53 53
54 54 def _add_ipython_dir(self, parser):
55 55 """Add the --ipython-dir option to the parser."""
56 56 paa = parser.add_argument
57 57 paa('--ipython-dir',
58 58 dest='Global.ipython_dir',type=unicode,
59 59 help=
60 60 """Set to override default location of the IPython directory
61 61 IPYTHON_DIR, stored as Global.ipython_dir. This can also be
62 62 specified through the environment variable IPYTHON_DIR.""",
63 63 metavar='Global.ipython_dir')
64 64
65 65 def _add_log_level(self, parser):
66 66 """Add the --log-level option to the parser."""
67 67 paa = parser.add_argument
68 68 paa('--log-level',
69 69 dest="Global.log_level",type=int,
70 70 help='Set the log level (0,10,20,30,40,50). Default is 30.',
71 71 metavar='Global.log_level')
72 72
73 73 def _add_version(self, parser):
74 74 """Add the --version option to the parser."""
75 75 parser.add_argument('--version', action="version",
76 76 version=self.version)
77 77
78 78 def _add_arguments(self):
79 79 self._add_ipython_dir(self.parser)
80 80 self._add_log_level(self.parser)
81 81 self._add_version(self.parser)
82 82
83 83
84 84 class Application(object):
85 85 """Load a config, construct configurables and set them running.
86 86
87 87 The configuration of an application can be done via three different Config
88 88 objects, which are loaded and ultimately merged into a single one used
89 89 from that point on by the app. These are:
90 90
91 91 1. default_config: internal defaults, implemented in code.
92 92 2. file_config: read from the filesystem.
93 93 3. command_line_config: read from the system's command line flags.
94 94
95 95 During initialization, 3 is actually read before 2, since at the
96 96 command-line one may override the location of the file to be read. But the
97 97 above is the order in which the merge is made.
98 98 """
99 99
100 100 name = u'ipython'
101 101 description = 'IPython: an enhanced interactive Python shell.'
102 102 #: Usage message printed by argparse. If None, auto-generate
103 103 usage = None
104 104 #: The command line config loader. Subclass of ArgParseConfigLoader.
105 105 command_line_loader = BaseAppConfigLoader
106 106 #: The name of the config file to load, determined at runtime
107 107 config_file_name = None
108 108 #: The name of the default config file. Track separately from the actual
109 109 #: name because some logic happens only if we aren't using the default.
110 110 default_config_file_name = u'ipython_config.py'
111 111 default_log_level = logging.WARN
112 112 #: Set by --profile option
113 113 profile_name = None
114 114 #: User's ipython directory, typically ~/.ipython or ~/.config/ipython/
115 115 ipython_dir = None
116 116 #: Internal defaults, implemented in code.
117 117 default_config = None
118 118 #: Read from the filesystem.
119 119 file_config = None
120 120 #: Read from the system's command line flags.
121 121 command_line_config = None
122 122 #: The final config that will be passed to the main object.
123 123 master_config = None
124 124 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
125 125 argv = None
126 126 #: extra arguments computed by the command-line loader
127 127 extra_args = None
128 128 #: The class to use as the crash handler.
129 129 crash_handler_class = crashhandler.CrashHandler
130 130
131 131 # Private attributes
132 132 _exiting = False
133 133 _initialized = False
134 134
135 135 def __init__(self, argv=None):
136 136 self.argv = sys.argv[1:] if argv is None else argv
137 137 self.init_logger()
138 138
139 139 def init_logger(self):
140 140 self.log = logging.getLogger(self.__class__.__name__)
141 141 # This is used as the default until the command line arguments are read.
142 142 self.log.setLevel(self.default_log_level)
143 143 self._log_handler = logging.StreamHandler()
144 144 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
145 145 self._log_handler.setFormatter(self._log_formatter)
146 146 self.log.addHandler(self._log_handler)
147 147
148 148 def _set_log_level(self, level):
149 149 self.log.setLevel(level)
150 150
151 151 def _get_log_level(self):
152 152 return self.log.level
153 153
154 154 log_level = property(_get_log_level, _set_log_level)
155 155
156 156 def initialize(self):
157 157 """Initialize the application.
158 158
159 159 Loads all configuration information and sets all application state, but
160 160 does not start any relevant processing (typically some kind of event
161 161 loop).
162 162
163 163 Once this method has been called, the application is flagged as
164 164 initialized and the method becomes a no-op."""
165 165
166 166 if self._initialized:
167 167 return
168 168
169 169 # The first part is protected with an 'attempt' wrapper, that will log
170 170 # failures with the basic system traceback machinery. Once our crash
171 171 # handler is in place, we can let any subsequent exception propagate,
172 172 # as our handler will log it with much better detail than the default.
173 173 self.attempt(self.create_crash_handler)
174 174
175 175 # Configuration phase
176 176 # Default config (internally hardwired in application code)
177 177 self.create_default_config()
178 178 self.log_default_config()
179 179 self.set_default_config_log_level()
180 180
181 181 # Command-line config
182 182 self.pre_load_command_line_config()
183 183 self.load_command_line_config()
184 184 self.set_command_line_config_log_level()
185 185 self.post_load_command_line_config()
186 186 self.log_command_line_config()
187 187
188 188 # Find resources needed for filesystem access, using information from
189 189 # the above two
190 190 self.find_ipython_dir()
191 191 self.find_resources()
192 192 self.find_config_file_name()
193 193 self.find_config_file_paths()
194 194
195 195 # File-based config
196 196 self.pre_load_file_config()
197 197 self.load_file_config()
198 198 self.set_file_config_log_level()
199 199 self.post_load_file_config()
200 200 self.log_file_config()
201 201
202 202 # Merge all config objects into a single one the app can then use
203 203 self.merge_configs()
204 204 self.log_master_config()
205 205
206 206 # Construction phase
207 207 self.pre_construct()
208 208 self.construct()
209 209 self.post_construct()
210 210
211 211 # Done, flag as such and
212 212 self._initialized = True
213 213
214 214 def start(self):
215 215 """Start the application."""
216 216 self.initialize()
217 217 self.start_app()
218 218
219 219 #-------------------------------------------------------------------------
220 220 # Various stages of Application creation
221 221 #-------------------------------------------------------------------------
222 222
223 223 def create_crash_handler(self):
224 224 """Create a crash handler, typically setting sys.excepthook to it."""
225 225 self.crash_handler = self.crash_handler_class(self)
226 226 sys.excepthook = self.crash_handler
227 227
228 228 def create_default_config(self):
229 229 """Create defaults that can't be set elsewhere.
230 230
231 231 For the most part, we try to set default in the class attributes
232 232 of Configurables. But, defaults the top-level Application (which is
233 233 not a HasTraits or Configurables) are not set in this way. Instead
234 234 we set them here. The Global section is for variables like this that
235 235 don't belong to a particular configurable.
236 236 """
237 237 c = Config()
238 238 c.Global.ipython_dir = get_ipython_dir()
239 239 c.Global.log_level = self.log_level
240 240 self.default_config = c
241 241
242 242 def log_default_config(self):
243 243 self.log.debug('Default config loaded:')
244 244 self.log.debug(repr(self.default_config))
245 245
246 246 def set_default_config_log_level(self):
247 247 try:
248 248 self.log_level = self.default_config.Global.log_level
249 249 except AttributeError:
250 250 # Fallback to the default_log_level class attribute
251 251 pass
252 252
253 253 def create_command_line_config(self):
254 254 """Create and return a command line config loader."""
255 255 return self.command_line_loader(
256 256 self.argv,
257 257 description=self.description,
258 258 version=release.version,
259 259 usage=self.usage
260 260 )
261 261
262 262 def pre_load_command_line_config(self):
263 263 """Do actions just before loading the command line config."""
264 264 pass
265 265
266 266 def load_command_line_config(self):
267 267 """Load the command line config."""
268 268 loader = self.create_command_line_config()
269 269 self.command_line_config = loader.load_config()
270 270 self.extra_args = loader.get_extra_args()
271 271
272 272 def set_command_line_config_log_level(self):
273 273 try:
274 274 self.log_level = self.command_line_config.Global.log_level
275 275 except AttributeError:
276 276 pass
277 277
278 278 def post_load_command_line_config(self):
279 279 """Do actions just after loading the command line config."""
280 280 pass
281 281
282 282 def log_command_line_config(self):
283 283 self.log.debug("Command line config loaded:")
284 284 self.log.debug(repr(self.command_line_config))
285 285
286 286 def find_ipython_dir(self):
287 287 """Set the IPython directory.
288 288
289 289 This sets ``self.ipython_dir``, but the actual value that is passed to
290 290 the application is kept in either ``self.default_config`` or
291 291 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
292 292 ``sys.path`` so config files there can be referenced by other config
293 293 files.
294 294 """
295 295
296 296 try:
297 297 self.ipython_dir = self.command_line_config.Global.ipython_dir
298 298 except AttributeError:
299 299 self.ipython_dir = self.default_config.Global.ipython_dir
300 300 sys.path.append(os.path.abspath(self.ipython_dir))
301 301 if not os.path.isdir(self.ipython_dir):
302 302 os.makedirs(self.ipython_dir, mode=0777)
303 303 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
304 304
305 305 def find_resources(self):
306 306 """Find other resources that need to be in place.
307 307
308 308 Things like cluster directories need to be in place to find the
309 309 config file. These happen right after the IPython directory has
310 310 been set.
311 311 """
312 312 pass
313 313
314 314 def find_config_file_name(self):
315 315 """Find the config file name for this application.
316 316
317 317 This must set ``self.config_file_name`` to the filename of the
318 318 config file to use (just the filename). The search paths for the
319 319 config file are set in :meth:`find_config_file_paths` and then passed
320 320 to the config file loader where they are resolved to an absolute path.
321 321
322 322 If a profile has been set at the command line, this will resolve it.
323 323 """
324 324 try:
325 325 self.config_file_name = self.command_line_config.Global.config_file
326 326 except AttributeError:
327 327 pass
328 328 else:
329 329 return
330 330
331 331 try:
332 332 self.profile_name = self.command_line_config.Global.profile
333 333 except AttributeError:
334 334 # Just use the default as there is no profile
335 335 self.config_file_name = self.default_config_file_name
336 336 else:
337 337 # Use the default config file name and profile name if set
338 338 # to determine the used config file name.
339 339 name_parts = self.default_config_file_name.split('.')
340 340 name_parts.insert(1, u'_' + self.profile_name + u'.')
341 341 self.config_file_name = ''.join(name_parts)
342 342
343 343 def find_config_file_paths(self):
344 344 """Set the search paths for resolving the config file.
345 345
346 346 This must set ``self.config_file_paths`` to a sequence of search
347 347 paths to pass to the config file loader.
348 348 """
349 349 # Include our own profiles directory last, so that users can still find
350 350 # our shipped copies of builtin profiles even if they don't have them
351 351 # in their local ipython directory.
352 352 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
353 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
353 self.config_file_paths = (os.getcwdu(), self.ipython_dir, prof_dir)
354 354
355 355 def pre_load_file_config(self):
356 356 """Do actions before the config file is loaded."""
357 357 pass
358 358
359 def load_file_config(self):
359 def load_file_config(self, suppress_errors=True):
360 360 """Load the config file.
361 361
362 362 This tries to load the config file from disk. If successful, the
363 363 ``CONFIG_FILE`` config variable is set to the resolved config file
364 364 location. If not successful, an empty config is used.
365 365 """
366 366 self.log.debug("Attempting to load config file: %s" %
367 367 self.config_file_name)
368 368 loader = PyFileConfigLoader(self.config_file_name,
369 369 path=self.config_file_paths)
370 370 try:
371 371 self.file_config = loader.load_config()
372 372 self.file_config.Global.config_file = loader.full_filename
373 373 except IOError:
374 374 # Only warn if the default config file was NOT being used.
375 375 if not self.config_file_name==self.default_config_file_name:
376 376 self.log.warn("Config file not found, skipping: %s" %
377 377 self.config_file_name, exc_info=True)
378 378 self.file_config = Config()
379 379 except:
380 if not suppress_errors: # For testing purposes
381 raise
380 382 self.log.warn("Error loading config file: %s" %
381 383 self.config_file_name, exc_info=True)
382 384 self.file_config = Config()
383 385
384 386 def set_file_config_log_level(self):
385 387 # We need to keeep self.log_level updated. But we only use the value
386 388 # of the file_config if a value was not specified at the command
387 389 # line, because the command line overrides everything.
388 390 if not hasattr(self.command_line_config.Global, 'log_level'):
389 391 try:
390 392 self.log_level = self.file_config.Global.log_level
391 393 except AttributeError:
392 394 pass # Use existing value
393 395
394 396 def post_load_file_config(self):
395 397 """Do actions after the config file is loaded."""
396 398 pass
397 399
398 400 def log_file_config(self):
399 401 if hasattr(self.file_config.Global, 'config_file'):
400 402 self.log.debug("Config file loaded: %s" %
401 403 self.file_config.Global.config_file)
402 404 self.log.debug(repr(self.file_config))
403 405
404 406 def merge_configs(self):
405 407 """Merge the default, command line and file config objects."""
406 408 config = Config()
407 409 config._merge(self.default_config)
408 410 config._merge(self.file_config)
409 411 config._merge(self.command_line_config)
410 412
411 413 # XXX fperez - propose to Brian we rename master_config to simply
412 414 # config, I think this is going to be heavily used in examples and
413 415 # application code and the name is shorter/easier to find/remember.
414 416 # For now, just alias it...
415 417 self.master_config = config
416 418 self.config = config
417 419
418 420 def log_master_config(self):
419 421 self.log.debug("Master config created:")
420 422 self.log.debug(repr(self.master_config))
421 423
422 424 def pre_construct(self):
423 425 """Do actions after the config has been built, but before construct."""
424 426 pass
425 427
426 428 def construct(self):
427 429 """Construct the main objects that make up this app."""
428 430 self.log.debug("Constructing main objects for application")
429 431
430 432 def post_construct(self):
431 433 """Do actions after construct, but before starting the app."""
432 434 pass
433 435
434 436 def start_app(self):
435 437 """Actually start the app."""
436 438 self.log.debug("Starting application")
437 439
438 440 #-------------------------------------------------------------------------
439 441 # Utility methods
440 442 #-------------------------------------------------------------------------
441 443
442 444 def exit(self, exit_status=0):
443 445 if self._exiting:
444 446 pass
445 447 else:
446 448 self.log.debug("Exiting application: %s" % self.name)
447 449 self._exiting = True
448 450 sys.exit(exit_status)
449 451
450 452 def attempt(self, func):
451 453 try:
452 454 func()
453 455 except SystemExit:
454 456 raise
455 457 except:
456 458 self.log.critical("Aborting application: %s" % self.name,
457 459 exc_info=True)
458 460 self.exit(0)
459 461
@@ -1,417 +1,417 b''
1 1 """Tests for various magic functions.
2 2
3 3 Needs to be run by nose (to make ipython session available).
4 4 """
5 5 from __future__ import absolute_import
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Imports
9 9 #-----------------------------------------------------------------------------
10 10
11 11 import os
12 12 import sys
13 13 import tempfile
14 14 import types
15 15 from cStringIO import StringIO
16 16
17 17 import nose.tools as nt
18 18
19 19 from IPython.utils.path import get_long_path_name
20 20 from IPython.testing import decorators as dec
21 21 from IPython.testing import tools as tt
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Test functions begin
25 25 #-----------------------------------------------------------------------------
26 26 def test_rehashx():
27 27 # clear up everything
28 28 _ip = get_ipython()
29 29 _ip.alias_manager.alias_table.clear()
30 30 del _ip.db['syscmdlist']
31 31
32 32 _ip.magic('rehashx')
33 33 # Practically ALL ipython development systems will have more than 10 aliases
34 34
35 35 yield (nt.assert_true, len(_ip.alias_manager.alias_table) > 10)
36 36 for key, val in _ip.alias_manager.alias_table.iteritems():
37 37 # we must strip dots from alias names
38 38 nt.assert_true('.' not in key)
39 39
40 40 # rehashx must fill up syscmdlist
41 41 scoms = _ip.db['syscmdlist']
42 42 yield (nt.assert_true, len(scoms) > 10)
43 43
44 44
45 45 def test_magic_parse_options():
46 46 """Test that we don't mangle paths when parsing magic options."""
47 47 ip = get_ipython()
48 48 path = 'c:\\x'
49 49 opts = ip.parse_options('-f %s' % path,'f:')[0]
50 50 # argv splitting is os-dependent
51 51 if os.name == 'posix':
52 52 expected = 'c:x'
53 53 else:
54 54 expected = path
55 55 nt.assert_equals(opts['f'], expected)
56 56
57 57
58 58 def doctest_hist_f():
59 59 """Test %hist -f with temporary filename.
60 60
61 61 In [9]: import tempfile
62 62
63 63 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
64 64
65 65 In [11]: %hist -nl -f $tfile 3
66 66
67 67 In [13]: import os; os.unlink(tfile)
68 68 """
69 69
70 70
71 71 def doctest_hist_r():
72 72 """Test %hist -r
73 73
74 74 XXX - This test is not recording the output correctly. For some reason, in
75 75 testing mode the raw history isn't getting populated. No idea why.
76 76 Disabling the output checking for now, though at least we do run it.
77 77
78 78 In [1]: 'hist' in _ip.lsmagic()
79 79 Out[1]: True
80 80
81 81 In [2]: x=1
82 82
83 83 In [3]: %hist -rl 2
84 84 x=1 # random
85 85 %hist -r 2
86 86 """
87 87
88 88 def doctest_hist_op():
89 89 """Test %hist -op
90 90
91 91 In [1]: class b:
92 92 ...: pass
93 93 ...:
94 94
95 95 In [2]: class s(b):
96 96 ...: def __str__(self):
97 97 ...: return 's'
98 98 ...:
99 99
100 100 In [3]:
101 101
102 102 In [4]: class r(b):
103 103 ...: def __repr__(self):
104 104 ...: return 'r'
105 105 ...:
106 106
107 107 In [5]: class sr(s,r): pass
108 108 ...:
109 109
110 110 In [6]:
111 111
112 112 In [7]: bb=b()
113 113
114 114 In [8]: ss=s()
115 115
116 116 In [9]: rr=r()
117 117
118 118 In [10]: ssrr=sr()
119 119
120 120 In [11]: bb
121 121 Out[11]: <...b instance at ...>
122 122
123 123 In [12]: ss
124 124 Out[12]: <...s instance at ...>
125 125
126 126 In [13]:
127 127
128 128 In [14]: %hist -op
129 129 >>> class b:
130 130 ... pass
131 131 ...
132 132 >>> class s(b):
133 133 ... def __str__(self):
134 134 ... return 's'
135 135 ...
136 136 >>>
137 137 >>> class r(b):
138 138 ... def __repr__(self):
139 139 ... return 'r'
140 140 ...
141 141 >>> class sr(s,r): pass
142 142 >>>
143 143 >>> bb=b()
144 144 >>> ss=s()
145 145 >>> rr=r()
146 146 >>> ssrr=sr()
147 147 >>> bb
148 148 <...b instance at ...>
149 149 >>> ss
150 150 <...s instance at ...>
151 151 >>>
152 152 """
153 153
154 154 def test_macro():
155 155 ip = get_ipython()
156 156 ip.history_manager.reset() # Clear any existing history.
157 157 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
158 158 for i, cmd in enumerate(cmds, start=1):
159 159 ip.history_manager.store_inputs(i, cmd)
160 160 ip.magic("macro test 1-3")
161 161 nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n")
162 162
163 163 # List macros.
164 164 assert "test" in ip.magic("macro")
165 165
166 166 def test_macro_run():
167 167 """Test that we can run a multi-line macro successfully."""
168 168 ip = get_ipython()
169 169 ip.history_manager.reset()
170 170 cmds = ["a=10", "a+=1", "print a", "%macro test 2-3"]
171 171 for cmd in cmds:
172 172 ip.run_cell(cmd)
173 173 nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint a\n")
174 174 original_stdout = sys.stdout
175 175 new_stdout = StringIO()
176 176 sys.stdout = new_stdout
177 177 try:
178 178 ip.run_cell("test")
179 179 nt.assert_true("12" in new_stdout.getvalue())
180 180 ip.run_cell("test")
181 181 nt.assert_true("13" in new_stdout.getvalue())
182 182 finally:
183 183 sys.stdout = original_stdout
184 184 new_stdout.close()
185 185
186 186
187 187 # XXX failing for now, until we get clearcmd out of quarantine. But we should
188 188 # fix this and revert the skip to happen only if numpy is not around.
189 189 #@dec.skipif_not_numpy
190 190 @dec.skip_known_failure
191 191 def test_numpy_clear_array_undec():
192 192 from IPython.extensions import clearcmd
193 193
194 194 _ip.ex('import numpy as np')
195 195 _ip.ex('a = np.empty(2)')
196 196 yield (nt.assert_true, 'a' in _ip.user_ns)
197 197 _ip.magic('clear array')
198 198 yield (nt.assert_false, 'a' in _ip.user_ns)
199 199
200 200
201 201 # Multiple tests for clipboard pasting
202 202 @dec.parametric
203 203 def test_paste():
204 204 _ip = get_ipython()
205 205 def paste(txt, flags='-q'):
206 206 """Paste input text, by default in quiet mode"""
207 207 hooks.clipboard_get = lambda : txt
208 208 _ip.magic('paste '+flags)
209 209
210 210 # Inject fake clipboard hook but save original so we can restore it later
211 211 hooks = _ip.hooks
212 212 user_ns = _ip.user_ns
213 213 original_clip = hooks.clipboard_get
214 214
215 215 try:
216 216 # This try/except with an emtpy except clause is here only because
217 217 # try/yield/finally is invalid syntax in Python 2.4. This will be
218 218 # removed when we drop 2.4-compatibility, and the emtpy except below
219 219 # will be changed to a finally.
220 220
221 221 # Run tests with fake clipboard function
222 222 user_ns.pop('x', None)
223 223 paste('x=1')
224 224 yield nt.assert_equal(user_ns['x'], 1)
225 225
226 226 user_ns.pop('x', None)
227 227 paste('>>> x=2')
228 228 yield nt.assert_equal(user_ns['x'], 2)
229 229
230 230 paste("""
231 231 >>> x = [1,2,3]
232 232 >>> y = []
233 233 >>> for i in x:
234 234 ... y.append(i**2)
235 235 ...
236 236 """)
237 237 yield nt.assert_equal(user_ns['x'], [1,2,3])
238 238 yield nt.assert_equal(user_ns['y'], [1,4,9])
239 239
240 240 # Now, test that paste -r works
241 241 user_ns.pop('x', None)
242 242 yield nt.assert_false('x' in user_ns)
243 243 _ip.magic('paste -r')
244 244 yield nt.assert_equal(user_ns['x'], [1,2,3])
245 245
246 246 # Also test paste echoing, by temporarily faking the writer
247 247 w = StringIO()
248 248 writer = _ip.write
249 249 _ip.write = w.write
250 250 code = """
251 251 a = 100
252 252 b = 200"""
253 253 try:
254 254 paste(code,'')
255 255 out = w.getvalue()
256 256 finally:
257 257 _ip.write = writer
258 258 yield nt.assert_equal(user_ns['a'], 100)
259 259 yield nt.assert_equal(user_ns['b'], 200)
260 260 yield nt.assert_equal(out, code+"\n## -- End pasted text --\n")
261 261
262 262 finally:
263 263 # This should be in a finally clause, instead of the bare except above.
264 264 # Restore original hook
265 265 hooks.clipboard_get = original_clip
266 266
267 267
268 268 def test_time():
269 269 _ip.magic('time None')
270 270
271 271
272 272 def doctest_time():
273 273 """
274 274 In [10]: %time None
275 275 CPU times: user 0.00 s, sys: 0.00 s, total: 0.00 s
276 276 Wall time: 0.00 s
277 277 """
278 278
279 279
280 280 def test_doctest_mode():
281 281 "Toggle doctest_mode twice, it should be a no-op and run without error"
282 282 _ip.magic('doctest_mode')
283 283 _ip.magic('doctest_mode')
284 284
285 285
286 286 def test_parse_options():
287 287 """Tests for basic options parsing in magics."""
288 288 # These are only the most minimal of tests, more should be added later. At
289 289 # the very least we check that basic text/unicode calls work OK.
290 290 nt.assert_equal(_ip.parse_options('foo', '')[1], 'foo')
291 291 nt.assert_equal(_ip.parse_options(u'foo', '')[1], u'foo')
292 292
293 293
294 294 def test_dirops():
295 295 """Test various directory handling operations."""
296 curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
296 curpath = lambda :os.path.splitdrive(os.getcwdu())[1].replace('\\','/')
297 297
298 startdir = os.getcwd()
298 startdir = os.getcwdu()
299 299 ipdir = _ip.ipython_dir
300 300 try:
301 301 _ip.magic('cd "%s"' % ipdir)
302 302 nt.assert_equal(curpath(), ipdir)
303 303 _ip.magic('cd -')
304 304 nt.assert_equal(curpath(), startdir)
305 305 _ip.magic('pushd "%s"' % ipdir)
306 306 nt.assert_equal(curpath(), ipdir)
307 307 _ip.magic('popd')
308 308 nt.assert_equal(curpath(), startdir)
309 309 finally:
310 310 os.chdir(startdir)
311 311
312 312
313 313 def check_cpaste(code, should_fail=False):
314 314 """Execute code via 'cpaste' and ensure it was executed, unless
315 315 should_fail is set.
316 316 """
317 317 _ip.user_ns['code_ran'] = False
318 318
319 319 src = StringIO()
320 320 src.write('\n')
321 321 src.write(code)
322 322 src.write('\n--\n')
323 323 src.seek(0)
324 324
325 325 stdin_save = sys.stdin
326 326 sys.stdin = src
327 327
328 328 try:
329 329 _ip.magic('cpaste')
330 330 except:
331 331 if not should_fail:
332 332 raise AssertionError("Failure not expected : '%s'" %
333 333 code)
334 334 else:
335 335 assert _ip.user_ns['code_ran']
336 336 if should_fail:
337 337 raise AssertionError("Failure expected : '%s'" % code)
338 338 finally:
339 339 sys.stdin = stdin_save
340 340
341 341
342 342 def test_cpaste():
343 343 """Test cpaste magic"""
344 344
345 345 def run():
346 346 """Marker function: sets a flag when executed.
347 347 """
348 348 _ip.user_ns['code_ran'] = True
349 349 return 'run' # return string so '+ run()' doesn't result in success
350 350
351 351 tests = {'pass': ["> > > run()",
352 352 ">>> > run()",
353 353 "+++ run()",
354 354 "++ run()",
355 355 " >>> run()"],
356 356
357 357 'fail': ["+ + run()",
358 358 " ++ run()"]}
359 359
360 360 _ip.user_ns['run'] = run
361 361
362 362 for code in tests['pass']:
363 363 check_cpaste(code)
364 364
365 365 for code in tests['fail']:
366 366 check_cpaste(code, should_fail=True)
367 367
368 368 def test_xmode():
369 369 # Calling xmode three times should be a no-op
370 370 xmode = _ip.InteractiveTB.mode
371 371 for i in range(3):
372 372 _ip.magic("xmode")
373 373 nt.assert_equal(_ip.InteractiveTB.mode, xmode)
374 374
375 375 def doctest_who():
376 376 """doctest for %who
377 377
378 378 In [1]: %reset -f
379 379
380 380 In [2]: alpha = 123
381 381
382 382 In [3]: beta = 'beta'
383 383
384 384 In [4]: %who int
385 385 alpha
386 386
387 387 In [5]: %who str
388 388 beta
389 389
390 390 In [6]: %whos
391 391 Variable Type Data/Info
392 392 ----------------------------
393 393 alpha int 123
394 394 beta str beta
395 395
396 396 In [7]: %who_ls
397 397 Out[7]: ['alpha', 'beta']
398 398 """
399 399
400 400 def doctest_precision():
401 401 """doctest for %precision
402 402
403 403 In [1]: f = get_ipython().shell.display_formatter.formatters['text/plain']
404 404
405 405 In [2]: %precision 5
406 406 Out[2]: '%.5f'
407 407
408 408 In [3]: f.float_format
409 409 Out[3]: '%.5f'
410 410
411 411 In [4]: %precision %e
412 412 Out[4]: '%e'
413 413
414 414 In [5]: f(3.1415927)
415 415 Out[5]: '3.141593e+00'
416 416 """
417 417
General Comments 0
You need to be logged in to leave comments. Login now