Show More
@@ -171,7 +171,7 b' class HistoryAccessor(Configurable):' | |||
|
171 | 171 | self.hist_file = self._get_hist_file_name(profile) |
|
172 | 172 | |
|
173 | 173 | if sqlite3 is None and self.enabled: |
|
174 |
warn("IPython History requires SQLite, your history will not be saved |
|
|
174 | warn("IPython History requires SQLite, your history will not be saved") | |
|
175 | 175 | self.enabled = False |
|
176 | 176 | |
|
177 | 177 | self.init_db() |
@@ -196,6 +196,13 b' class InteractiveShell(SingletonConfigurable):' | |||
|
196 | 196 | """An enhanced, interactive shell for Python.""" |
|
197 | 197 | |
|
198 | 198 | _instance = None |
|
199 | ||
|
200 | ast_transformers = List([], config=True, help= | |
|
201 | """ | |
|
202 | A list of ast.NodeTransformer subclass instances, which will be applied | |
|
203 | to user input before code is run. | |
|
204 | """ | |
|
205 | ) | |
|
199 | 206 | |
|
200 | 207 | autocall = Enum((0,1,2), default_value=0, config=True, help= |
|
201 | 208 | """ |
@@ -326,7 +333,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
326 | 333 | 'prompt_out' : 'out_template', |
|
327 | 334 | 'prompts_pad_left' : 'justify', |
|
328 | 335 | } |
|
329 |
warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname} |
|
|
336 | warn("InteractiveShell.{name} is deprecated, use PromptManager.{newname}".format( | |
|
330 | 337 | name=name, newname=table[name]) |
|
331 | 338 | ) |
|
332 | 339 | # protect against weird cases where self.config may not exist: |
@@ -709,7 +716,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
709 | 716 | return |
|
710 | 717 | |
|
711 | 718 | warn("Attempting to work in a virtualenv. If you encounter problems, please " |
|
712 |
"install IPython inside the virtualenv. |
|
|
719 | "install IPython inside the virtualenv.") | |
|
713 | 720 | if sys.platform == "win32": |
|
714 | 721 | virtual_env = os.path.join(os.environ['VIRTUAL_ENV'], 'Lib', 'site-packages') |
|
715 | 722 | else: |
@@ -2611,6 +2618,8 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2611 | 2618 | self.execution_count += 1 |
|
2612 | 2619 | return None |
|
2613 | 2620 | |
|
2621 | code_ast = self.transform_ast(code_ast) | |
|
2622 | ||
|
2614 | 2623 | interactivity = "none" if silent else self.ast_node_interactivity |
|
2615 | 2624 | self.run_ast_nodes(code_ast.body, cell_name, |
|
2616 | 2625 | interactivity=interactivity) |
@@ -2643,6 +2652,31 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2643 | 2652 | self.history_manager.store_output(self.execution_count) |
|
2644 | 2653 | # Each cell is a *single* input, regardless of how many lines it has |
|
2645 | 2654 | self.execution_count += 1 |
|
2655 | ||
|
2656 | def transform_ast(self, node): | |
|
2657 | """Apply the AST transformations from self.ast_transformers | |
|
2658 | ||
|
2659 | Parameters | |
|
2660 | ---------- | |
|
2661 | node : ast.Node | |
|
2662 | The root node to be transformed. Typically called with the ast.Module | |
|
2663 | produced by parsing user input. | |
|
2664 | ||
|
2665 | Returns | |
|
2666 | ------- | |
|
2667 | An ast.Node corresponding to the node it was called with. Note that it | |
|
2668 | may also modify the passed object, so don't rely on references to the | |
|
2669 | original AST. | |
|
2670 | """ | |
|
2671 | for transformer in self.ast_transformers: | |
|
2672 | try: | |
|
2673 | node = transformer.visit(node) | |
|
2674 | except Exception: | |
|
2675 | warn("AST transformer %r threw an error. It will be unregistered." % transformer) | |
|
2676 | self.ast_transformers.remove(transformer) | |
|
2677 | ||
|
2678 | return ast.fix_missing_locations(node) | |
|
2679 | ||
|
2646 | 2680 | |
|
2647 | 2681 | def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'): |
|
2648 | 2682 | """Run a sequence of AST nodes. The execution mode depends on the |
@@ -14,6 +14,7 b'' | |||
|
14 | 14 | |
|
15 | 15 | # Stdlib |
|
16 | 16 | import __builtin__ as builtin_mod |
|
17 | import ast | |
|
17 | 18 | import bdb |
|
18 | 19 | import os |
|
19 | 20 | import sys |
@@ -781,26 +782,54 b' python-profiler package from non-free.""")' | |||
|
781 | 782 | # but is there a better way to achieve that the code stmt has access |
|
782 | 783 | # to the shell namespace? |
|
783 | 784 | transform = self.shell.input_splitter.transform_cell |
|
785 | ||
|
784 | 786 | if cell is None: |
|
785 | 787 | # called as line magic |
|
786 |
setup = |
|
|
787 |
stmt = |
|
|
788 | else: | |
|
789 | setup = timeit.reindent(transform(stmt), 4) | |
|
790 | stmt = timeit.reindent(transform(cell), 8) | |
|
791 | ||
|
792 | # From Python 3.3, this template uses new-style string formatting. | |
|
793 | if sys.version_info >= (3, 3): | |
|
794 | src = timeit.template.format(stmt=stmt, setup=setup) | |
|
788 | ast_setup = ast.parse("pass") | |
|
789 | ast_stmt = ast.parse(transform(stmt)) | |
|
795 | 790 | else: |
|
796 | src = timeit.template % dict(stmt=stmt, setup=setup) | |
|
791 | ast_setup = ast.parse(transform(stmt)) | |
|
792 | ast_stmt = ast.parse(transform(cell)) | |
|
793 | ||
|
794 | ast_setup = self.shell.transform_ast(ast_setup) | |
|
795 | ast_stmt = self.shell.transform_ast(ast_stmt) | |
|
796 | ||
|
797 | # This codestring is taken from timeit.template - we fill it in as an | |
|
798 | # AST, so that we can apply our AST transformations to the user code | |
|
799 | # without affecting the timing code. | |
|
800 | timeit_ast_template = ast.parse('def inner(_it, _timer):\n' | |
|
801 | ' setup\n' | |
|
802 | ' _t0 = _timer()\n' | |
|
803 | ' for _i in _it:\n' | |
|
804 | ' stmt\n' | |
|
805 | ' _t1 = _timer()\n' | |
|
806 | ' return _t1 - _t0\n') | |
|
807 | ||
|
808 | class TimeitTemplateFiller(ast.NodeTransformer): | |
|
809 | "This is quite tightly tied to the template definition above." | |
|
810 | def visit_FunctionDef(self, node): | |
|
811 | "Fill in the setup statement" | |
|
812 | self.generic_visit(node) | |
|
813 | if node.name == "inner": | |
|
814 | node.body[:1] = ast_setup.body | |
|
815 | ||
|
816 | return node | |
|
817 | ||
|
818 | def visit_For(self, node): | |
|
819 | "Fill in the statement to be timed" | |
|
820 | if getattr(getattr(node.body[0], 'value', None), 'id', None) == 'stmt': | |
|
821 | node.body = ast_stmt.body | |
|
822 | return node | |
|
823 | ||
|
824 | timeit_ast = TimeitTemplateFiller().visit(timeit_ast_template) | |
|
825 | timeit_ast = ast.fix_missing_locations(timeit_ast) | |
|
797 | 826 | |
|
798 | 827 | # Track compilation time so it can be reported if too long |
|
799 | 828 | # Minimum time above which compilation time will be reported |
|
800 | 829 | tc_min = 0.1 |
|
801 | 830 | |
|
802 | 831 | t0 = clock() |
|
803 |
code = compile( |
|
|
832 | code = compile(timeit_ast, "<magic-timeit>", "exec") | |
|
804 | 833 | tc = clock()-t0 |
|
805 | 834 | |
|
806 | 835 | ns = {} |
@@ -884,20 +913,31 b' python-profiler package from non-free.""")' | |||
|
884 | 913 | # fail immediately if the given expression can't be compiled |
|
885 | 914 | |
|
886 | 915 | expr = self.shell.prefilter(parameter_s,False) |
|
916 | ||
|
917 | # Minimum time above which parse time will be reported | |
|
918 | tp_min = 0.1 | |
|
919 | ||
|
920 | t0 = clock() | |
|
921 | expr_ast = ast.parse(expr) | |
|
922 | tp = clock()-t0 | |
|
923 | ||
|
924 | # Apply AST transformations | |
|
925 | expr_ast = self.shell.transform_ast(expr_ast) | |
|
887 | 926 | |
|
888 | 927 | # Minimum time above which compilation time will be reported |
|
889 | 928 | tc_min = 0.1 |
|
890 | 929 | |
|
891 | try: | |
|
930 | if len(expr_ast.body)==1 and isinstance(expr_ast.body[0], ast.Expr): | |
|
892 | 931 | mode = 'eval' |
|
893 | t0 = clock() | |
|
894 | code = compile(expr,'<timed eval>',mode) | |
|
895 | tc = clock()-t0 | |
|
896 | except SyntaxError: | |
|
932 | source = '<timed eval>' | |
|
933 | expr_ast = ast.Expression(expr_ast.body[0].value) | |
|
934 | else: | |
|
897 | 935 | mode = 'exec' |
|
898 | t0 = clock() | |
|
899 | code = compile(expr,'<timed exec>',mode) | |
|
900 | tc = clock()-t0 | |
|
936 | source = '<timed exec>' | |
|
937 | t0 = clock() | |
|
938 | code = compile(expr_ast, source, mode) | |
|
939 | tc = clock()-t0 | |
|
940 | ||
|
901 | 941 | # skew measurement as little as possible |
|
902 | 942 | glob = self.shell.user_ns |
|
903 | 943 | wtime = time.time |
@@ -923,6 +963,8 b' python-profiler package from non-free.""")' | |||
|
923 | 963 | print "Wall time: %.2f s" % wall_time |
|
924 | 964 | if tc > tc_min: |
|
925 | 965 | print "Compiler : %.2f s" % tc |
|
966 | if tp > tp_min: | |
|
967 | print "Parser : %.2f s" % tp | |
|
926 | 968 | return out |
|
927 | 969 | |
|
928 | 970 | @skip_doctest |
@@ -20,6 +20,7 b' Authors' | |||
|
20 | 20 | # Imports |
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | # stdlib |
|
23 | import ast | |
|
23 | 24 | import os |
|
24 | 25 | import shutil |
|
25 | 26 | import sys |
@@ -426,6 +427,132 b' class TestModules(unittest.TestCase, tt.TempFileMixin):' | |||
|
426 | 427 | out = "False\nFalse\nFalse\n" |
|
427 | 428 | tt.ipexec_validate(self.fname, out) |
|
428 | 429 | |
|
430 | class Negator(ast.NodeTransformer): | |
|
431 | """Negates all number literals in an AST.""" | |
|
432 | def visit_Num(self, node): | |
|
433 | node.n = -node.n | |
|
434 | return node | |
|
435 | ||
|
436 | class TestAstTransform(unittest.TestCase): | |
|
437 | def setUp(self): | |
|
438 | self.negator = Negator() | |
|
439 | ip.ast_transformers.append(self.negator) | |
|
440 | ||
|
441 | def tearDown(self): | |
|
442 | ip.ast_transformers.remove(self.negator) | |
|
443 | ||
|
444 | def test_run_cell(self): | |
|
445 | with tt.AssertPrints('-34'): | |
|
446 | ip.run_cell('print (12 + 22)') | |
|
447 | ||
|
448 | # A named reference to a number shouldn't be transformed. | |
|
449 | ip.user_ns['n'] = 55 | |
|
450 | with tt.AssertNotPrints('-55'): | |
|
451 | ip.run_cell('print (n)') | |
|
452 | ||
|
453 | def test_timeit(self): | |
|
454 | called = set() | |
|
455 | def f(x): | |
|
456 | called.add(x) | |
|
457 | ip.push({'f':f}) | |
|
458 | ||
|
459 | with tt.AssertPrints("best of "): | |
|
460 | ip.run_line_magic("timeit", "-n1 f(1)") | |
|
461 | self.assertEqual(called, set([-1])) | |
|
462 | called.clear() | |
|
463 | ||
|
464 | with tt.AssertPrints("best of "): | |
|
465 | ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)") | |
|
466 | self.assertEqual(called, set([-2, -3])) | |
|
467 | ||
|
468 | def test_time(self): | |
|
469 | called = [] | |
|
470 | def f(x): | |
|
471 | called.append(x) | |
|
472 | ip.push({'f':f}) | |
|
473 | ||
|
474 | # Test with an expression | |
|
475 | with tt.AssertPrints("CPU times"): | |
|
476 | ip.run_line_magic("time", "f(5+9)") | |
|
477 | self.assertEqual(called, [-14]) | |
|
478 | called[:] = [] | |
|
479 | ||
|
480 | # Test with a statement (different code path) | |
|
481 | with tt.AssertPrints("CPU times"): | |
|
482 | ip.run_line_magic("time", "a = f(-3 + -2)") | |
|
483 | self.assertEqual(called, [5]) | |
|
484 | ||
|
485 | def test_macro(self): | |
|
486 | ip.push({'a':10}) | |
|
487 | # The AST transformation makes this do a+=-1 | |
|
488 | ip.define_macro("amacro", "a+=1\nprint(a)") | |
|
489 | ||
|
490 | with tt.AssertPrints("9"): | |
|
491 | ip.run_cell("amacro") | |
|
492 | with tt.AssertPrints("8"): | |
|
493 | ip.run_cell("amacro") | |
|
494 | ||
|
495 | class IntegerWrapper(ast.NodeTransformer): | |
|
496 | """Wraps all integers in a call to Integer()""" | |
|
497 | def visit_Num(self, node): | |
|
498 | if isinstance(node.n, int): | |
|
499 | return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()), | |
|
500 | args=[node], keywords=[]) | |
|
501 | return node | |
|
502 | ||
|
503 | class TestAstTransform2(unittest.TestCase): | |
|
504 | def setUp(self): | |
|
505 | self.intwrapper = IntegerWrapper() | |
|
506 | ip.ast_transformers.append(self.intwrapper) | |
|
507 | ||
|
508 | self.calls = [] | |
|
509 | def Integer(*args): | |
|
510 | self.calls.append(args) | |
|
511 | return args | |
|
512 | ip.push({"Integer": Integer}) | |
|
513 | ||
|
514 | def tearDown(self): | |
|
515 | ip.ast_transformers.remove(self.intwrapper) | |
|
516 | del ip.user_ns['Integer'] | |
|
517 | ||
|
518 | def test_run_cell(self): | |
|
519 | ip.run_cell("n = 2") | |
|
520 | self.assertEqual(self.calls, [(2,)]) | |
|
521 | ||
|
522 | # This shouldn't throw an error | |
|
523 | ip.run_cell("o = 2.0") | |
|
524 | self.assertEqual(ip.user_ns['o'], 2.0) | |
|
525 | ||
|
526 | def test_timeit(self): | |
|
527 | called = set() | |
|
528 | def f(x): | |
|
529 | called.add(x) | |
|
530 | ip.push({'f':f}) | |
|
531 | ||
|
532 | with tt.AssertPrints("best of "): | |
|
533 | ip.run_line_magic("timeit", "-n1 f(1)") | |
|
534 | self.assertEqual(called, set([(1,)])) | |
|
535 | called.clear() | |
|
536 | ||
|
537 | with tt.AssertPrints("best of "): | |
|
538 | ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)") | |
|
539 | self.assertEqual(called, set([(2,), (3,)])) | |
|
540 | ||
|
541 | class ErrorTransformer(ast.NodeTransformer): | |
|
542 | """Throws an error when it sees a number.""" | |
|
543 | def visit_Num(self): | |
|
544 | raise ValueError("test") | |
|
545 | ||
|
546 | class TestAstTransformError(unittest.TestCase): | |
|
547 | def test_unregistering(self): | |
|
548 | err_transformer = ErrorTransformer() | |
|
549 | ip.ast_transformers.append(err_transformer) | |
|
550 | ||
|
551 | with tt.AssertPrints("unregister", channel='stderr'): | |
|
552 | ip.run_cell("1 + 2") | |
|
553 | ||
|
554 | # This should have been removed. | |
|
555 | nt.assert_not_in(err_transformer, ip.ast_transformers) | |
|
429 | 556 | |
|
430 | 557 | def test__IPYTHON__(): |
|
431 | 558 | # This shouldn't raise a NameError, that's all |
@@ -351,7 +351,7 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):' | |||
|
351 | 351 | """Replace --pylab='inline' with --pylab='auto'""" |
|
352 | 352 | if new == 'inline': |
|
353 | 353 | warn.warn("'inline' not available as pylab backend, " |
|
354 |
"using 'auto' instead. |
|
|
354 | "using 'auto' instead.") | |
|
355 | 355 | self.pylab = 'auto' |
|
356 | 356 | |
|
357 | 357 | def start(self): |
@@ -105,7 +105,7 b' class InputHookManager(object):' | |||
|
105 | 105 | |
|
106 | 106 | def __init__(self): |
|
107 | 107 | if ctypes is None: |
|
108 |
warn("IPython GUI event loop requires ctypes, %gui will not be available |
|
|
108 | warn("IPython GUI event loop requires ctypes, %gui will not be available") | |
|
109 | 109 | return |
|
110 | 110 | self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int) |
|
111 | 111 | self._apps = {} |
@@ -319,7 +319,7 b' def make_exclude():' | |||
|
319 | 319 | continue |
|
320 | 320 | fullpath = pjoin(parent, exclusion) |
|
321 | 321 | if not os.path.exists(fullpath) and not glob.glob(fullpath + '.*'): |
|
322 |
warn("Excluding nonexistent file: %r |
|
|
322 | warn("Excluding nonexistent file: %r" % exclusion) | |
|
323 | 323 | |
|
324 | 324 | return exclusions |
|
325 | 325 |
@@ -130,9 +130,9 b' def import_fail_info(mod_name,fns=None):' | |||
|
130 | 130 | """Inform load failure for a module.""" |
|
131 | 131 | |
|
132 | 132 | if fns == None: |
|
133 |
warn("Loading of %s failed. |
|
|
133 | warn("Loading of %s failed." % (mod_name,)) | |
|
134 | 134 | else: |
|
135 |
warn("Loading of %s from %s failed. |
|
|
135 | warn("Loading of %s from %s failed." % (fns,mod_name)) | |
|
136 | 136 | |
|
137 | 137 | |
|
138 | 138 | class NotGiven: pass |
@@ -42,7 +42,7 b' def warn(msg,level=2,exit_val=1):' | |||
|
42 | 42 | |
|
43 | 43 | if level>0: |
|
44 | 44 | header = ['','','WARNING: ','ERROR: ','FATAL ERROR: '] |
|
45 | io.stderr.write('%s%s' % (header[level],msg)) | |
|
45 | print(header[level], msg, sep='', file=io.stderr) | |
|
46 | 46 | if level == 4: |
|
47 | 47 | print('Exiting.\n', file=io.stderr) |
|
48 | 48 | sys.exit(exit_val) |
General Comments 0
You need to be logged in to leave comments.
Login now