Show More
@@ -77,8 +77,7 b' python-profiler package from non-free.""")' | |||||
77 |
|
77 | |||
78 | @skip_doctest |
|
78 | @skip_doctest | |
79 | @line_cell_magic |
|
79 | @line_cell_magic | |
80 |
def prun(self, parameter_s='', cell=None |
|
80 | def prun(self, parameter_s='', cell=None): | |
81 | opts=None,arg_lst=None,prog_ns=None): |
|
|||
82 |
|
81 | |||
83 | """Run a statement through the python code profiler. |
|
82 | """Run a statement through the python code profiler. | |
84 |
|
83 | |||
@@ -178,38 +177,33 b' python-profiler package from non-free.""")' | |||||
178 |
|
177 | |||
179 | In [1]: import profile; profile.help() |
|
178 | In [1]: import profile; profile.help() | |
180 | """ |
|
179 | """ | |
181 |
|
||||
182 | opts_def = Struct(D=[''],l=[],s=['time'],T=['']) |
|
|||
183 |
|
||||
184 | if user_mode: # regular user call |
|
|||
185 |
|
|
180 | opts, arg_str = self.parse_options(parameter_s, 'D:l:rs:T:q', | |
186 |
|
|
181 | list_all=True, posix=False) | |
187 | namespace = self.shell.user_ns |
|
|||
188 |
|
|
182 | if cell is not None: | |
189 |
|
|
183 | arg_str += '\n' + cell | |
190 | else: # called to run a program by %run -p |
|
184 | return self._run_with_profiler(arg_str, opts, self.shell.user_ns) | |
191 | try: |
|
|||
192 | filename = get_py_filename(arg_lst[0]) |
|
|||
193 | except IOError as e: |
|
|||
194 | try: |
|
|||
195 | msg = str(e) |
|
|||
196 | except UnicodeError: |
|
|||
197 | msg = e.message |
|
|||
198 | error(msg) |
|
|||
199 | return |
|
|||
200 |
|
185 | |||
201 | arg_str = 'execfile(filename,prog_ns)' |
|
186 | def _run_with_profiler(self, code, opts, namespace): | |
202 | namespace = { |
|
187 | """ | |
203 | 'execfile': self.shell.safe_execfile, |
|
188 | Run `code` with profiler. Used by ``%prun`` and ``%run -p``. | |
204 | 'prog_ns': prog_ns, |
|
189 | ||
205 | 'filename': filename |
|
190 | Parameters | |
206 | } |
|
191 | ---------- | |
|
192 | code : str | |||
|
193 | Code to be executed. | |||
|
194 | opts : Struct | |||
|
195 | Options parsed by `self.parse_options`. | |||
|
196 | namespace : dict | |||
|
197 | A dictionary for Python namespace (e.g., `self.shell.user_ns`). | |||
207 |
|
|
198 | ||
208 | opts.merge(opts_def) |
|
199 | """ | |
|
200 | ||||
|
201 | # Fill default values for unspecified options: | |||
|
202 | opts.merge(Struct(D=[''], l=[], s=['time'], T=[''])) | |||
209 |
|
203 | |||
210 | prof = profile.Profile() |
|
204 | prof = profile.Profile() | |
211 | try: |
|
205 | try: | |
212 |
prof = prof.runctx( |
|
206 | prof = prof.runctx(code, namespace, namespace) | |
213 | sys_exit = '' |
|
207 | sys_exit = '' | |
214 | except SystemExit: |
|
208 | except SystemExit: | |
215 | sys_exit = """*** SystemExit exception caught in code being profiled.""" |
|
209 | sys_exit = """*** SystemExit exception caught in code being profiled.""" | |
@@ -327,8 +321,10 b' python-profiler package from non-free.""")' | |||||
327 | file_finder=get_py_filename): |
|
321 | file_finder=get_py_filename): | |
328 | """Run the named file inside IPython as a program. |
|
322 | """Run the named file inside IPython as a program. | |
329 |
|
323 | |||
330 |
Usage: |
|
324 | Usage: | |
331 | %run [-n -i -t [-N<N>] -d [-b<N>] -p [profile options] -G] file [args] |
|
325 | %run [-n -i -e -G] | |
|
326 | [( -t [-N<N>] | -d [-b<N>] | -p [profile options] )] | |||
|
327 | ( -m mod | file ) [args] | |||
332 |
|
328 | |||
333 | Parameters after the filename are passed as command-line arguments to |
|
329 | Parameters after the filename are passed as command-line arguments to | |
334 | the program (put in sys.argv). Then, control returns to IPython's |
|
330 | the program (put in sys.argv). Then, control returns to IPython's | |
@@ -541,13 +537,123 b' python-profiler package from non-free.""")' | |||||
541 | # every single object ever created. |
|
537 | # every single object ever created. | |
542 | sys.modules[main_mod_name] = main_mod |
|
538 | sys.modules[main_mod_name] = main_mod | |
543 |
|
539 | |||
|
540 | if 'p' in opts or 'd' in opts: | |||
|
541 | if 'm' in opts: | |||
|
542 | code = 'run_module(modulename, prog_ns)' | |||
|
543 | code_ns = { | |||
|
544 | 'run_module': self.shell.safe_run_module, | |||
|
545 | 'prog_ns': prog_ns, | |||
|
546 | 'modulename': modulename, | |||
|
547 | } | |||
|
548 | else: | |||
|
549 | code = 'execfile(filename, prog_ns)' | |||
|
550 | code_ns = { | |||
|
551 | 'execfile': self.shell.safe_execfile, | |||
|
552 | 'prog_ns': prog_ns, | |||
|
553 | 'filename': get_py_filename(filename), | |||
|
554 | } | |||
|
555 | ||||
544 | try: |
|
556 | try: | |
545 | stats = None |
|
557 | stats = None | |
546 | with self.shell.readline_no_record: |
|
558 | with self.shell.readline_no_record: | |
547 | if 'p' in opts: |
|
559 | if 'p' in opts: | |
548 |
stats = self. |
|
560 | stats = self._run_with_profiler(code, opts, code_ns) | |
549 | else: |
|
561 | else: | |
550 | if 'd' in opts: |
|
562 | if 'd' in opts: | |
|
563 | self._run_with_debugger( | |||
|
564 | code, code_ns, opts.get('b', ['1'])[0], filename) | |||
|
565 | else: | |||
|
566 | if 'm' in opts: | |||
|
567 | def run(): | |||
|
568 | self.shell.safe_run_module(modulename, prog_ns) | |||
|
569 | else: | |||
|
570 | if runner is None: | |||
|
571 | runner = self.default_runner | |||
|
572 | if runner is None: | |||
|
573 | runner = self.shell.safe_execfile | |||
|
574 | ||||
|
575 | def run(): | |||
|
576 | runner(filename, prog_ns, prog_ns, | |||
|
577 | exit_ignore=exit_ignore) | |||
|
578 | ||||
|
579 | if 't' in opts: | |||
|
580 | # timed execution | |||
|
581 | try: | |||
|
582 | nruns = int(opts['N'][0]) | |||
|
583 | if nruns < 1: | |||
|
584 | error('Number of runs must be >=1') | |||
|
585 | return | |||
|
586 | except (KeyError): | |||
|
587 | nruns = 1 | |||
|
588 | self._run_with_timing(run, nruns) | |||
|
589 | else: | |||
|
590 | # regular execution | |||
|
591 | run() | |||
|
592 | ||||
|
593 | if 'i' in opts: | |||
|
594 | self.shell.user_ns['__name__'] = __name__save | |||
|
595 | else: | |||
|
596 | # The shell MUST hold a reference to prog_ns so after %run | |||
|
597 | # exits, the python deletion mechanism doesn't zero it out | |||
|
598 | # (leaving dangling references). | |||
|
599 | self.shell.cache_main_mod(prog_ns, filename) | |||
|
600 | # update IPython interactive namespace | |||
|
601 | ||||
|
602 | # Some forms of read errors on the file may mean the | |||
|
603 | # __name__ key was never set; using pop we don't have to | |||
|
604 | # worry about a possible KeyError. | |||
|
605 | prog_ns.pop('__name__', None) | |||
|
606 | ||||
|
607 | with preserve_keys(self.shell.user_ns, '__file__'): | |||
|
608 | self.shell.user_ns.update(prog_ns) | |||
|
609 | finally: | |||
|
610 | # It's a bit of a mystery why, but __builtins__ can change from | |||
|
611 | # being a module to becoming a dict missing some key data after | |||
|
612 | # %run. As best I can see, this is NOT something IPython is doing | |||
|
613 | # at all, and similar problems have been reported before: | |||
|
614 | # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html | |||
|
615 | # Since this seems to be done by the interpreter itself, the best | |||
|
616 | # we can do is to at least restore __builtins__ for the user on | |||
|
617 | # exit. | |||
|
618 | self.shell.user_ns['__builtins__'] = builtin_mod | |||
|
619 | ||||
|
620 | # Ensure key global structures are restored | |||
|
621 | sys.argv = save_argv | |||
|
622 | if restore_main: | |||
|
623 | sys.modules['__main__'] = restore_main | |||
|
624 | else: | |||
|
625 | # Remove from sys.modules the reference to main_mod we'd | |||
|
626 | # added. Otherwise it will trap references to objects | |||
|
627 | # contained therein. | |||
|
628 | del sys.modules[main_mod_name] | |||
|
629 | ||||
|
630 | return stats | |||
|
631 | ||||
|
632 | def _run_with_debugger(self, code, code_ns, break_point, filename): | |||
|
633 | """ | |||
|
634 | Run `code` in debugger with a break point. | |||
|
635 | ||||
|
636 | Parameters | |||
|
637 | ---------- | |||
|
638 | code : str | |||
|
639 | Code to execute. | |||
|
640 | code_ns : dict | |||
|
641 | A namespace in which `code` is executed. | |||
|
642 | break_point : str | |||
|
643 | Line number in the file specified by `filename` argument | |||
|
644 | or a string in the format ``file:line``. In the latter | |||
|
645 | case, `filename` is ignored. | |||
|
646 | See also :func:`.parse_breakpoint`. | |||
|
647 | filename : str | |||
|
648 | Path to the file in which break point is specified. | |||
|
649 | ||||
|
650 | Raises | |||
|
651 | ------ | |||
|
652 | UsageError | |||
|
653 | If no meaningful break point is given by `break_point` and | |||
|
654 | `filename`. | |||
|
655 | ||||
|
656 | """ | |||
551 |
|
|
657 | deb = debugger.Pdb(self.shell.colors) | |
552 |
|
|
658 | # reset Breakpoint state, which is moronically kept | |
553 |
|
|
659 | # in a class | |
@@ -556,7 +662,7 b' python-profiler package from non-free.""")' | |||||
556 |
|
|
662 | bdb.Breakpoint.bpbynumber = [None] | |
557 |
|
|
663 | # Set an initial breakpoint to stop execution | |
558 |
|
|
664 | maxtries = 10 | |
559 |
|
|
665 | bp_file, bp_line = parse_breakpoint(break_point, filename) | |
560 |
|
|
666 | checkline = deb.checkline(bp_file, bp_line) | |
561 |
|
|
667 | if not checkline: | |
562 |
|
|
668 | for bp in range(bp_line + 1, bp_line + maxtries + 1): | |
@@ -568,8 +674,7 b' python-profiler package from non-free.""")' | |||||
568 |
|
|
674 | "after trying up to line: %s.\n" | |
569 |
|
|
675 | "Please set a valid breakpoint manually " | |
570 |
|
|
676 | "with the -b option." % bp) | |
571 |
|
|
677 | raise UsageError(msg) | |
572 | return |
|
|||
573 |
|
|
678 | # if we find a good linenumber, set the breakpoint | |
574 |
|
|
679 | deb.do_break('%s:%s' % (bp_file, bp_line)) | |
575 |
|
680 | |||
@@ -580,11 +685,10 b' python-profiler package from non-free.""")' | |||||
580 |
|
|
685 | # Start file run | |
581 |
|
|
686 | print "NOTE: Enter 'c' at the", | |
582 |
|
|
687 | print "%s prompt to start your script." % deb.prompt | |
583 | ns = {'execfile': py3compat.execfile, 'prog_ns': prog_ns} |
|
|||
584 |
|
|
688 | try: | |
585 |
|
|
689 | #save filename so it can be used by methods on the deb object | |
586 |
|
|
690 | deb._exec_filename = filename | |
587 | deb.run('execfile("%s", prog_ns)' % filename, ns) |
|
691 | deb.run(code, code_ns) | |
588 |
|
692 | |||
589 |
|
|
693 | except: | |
590 |
|
|
694 | etype, value, tb = sys.exc_info() | |
@@ -592,25 +696,24 b' python-profiler package from non-free.""")' | |||||
592 |
|
|
696 | # one inside bdb.py, and the command-line typed by the | |
593 |
|
|
697 | # user (run by exec in pdb itself). | |
594 |
|
|
698 | self.shell.InteractiveTB(etype, value, tb, tb_offset=3) | |
595 | else: |
|
699 | ||
596 | if runner is None: |
|
700 | @staticmethod | |
597 | runner = self.default_runner |
|
701 | def _run_with_timing(run, nruns): | |
598 | if runner is None: |
|
702 | """ | |
599 | runner = self.shell.safe_execfile |
|
703 | Run function `run` and print timing information. | |
600 | if 't' in opts: |
|
704 | ||
601 | # timed execution |
|
705 | Parameters | |
602 | try: |
|
706 | ---------- | |
603 | nruns = int(opts['N'][0]) |
|
707 | run : callable | |
604 | if nruns < 1: |
|
708 | Any callable object which takes no argument. | |
605 | error('Number of runs must be >=1') |
|
709 | nruns : int | |
606 | return |
|
710 | Number of times to execute `run`. | |
607 | except (KeyError): |
|
711 | ||
608 | nruns = 1 |
|
712 | """ | |
609 |
|
|
713 | twall0 = time.time() | |
610 |
|
|
714 | if nruns == 1: | |
611 |
|
|
715 | t0 = clock2() | |
612 | runner(filename, prog_ns, prog_ns, |
|
716 | run() | |
613 | exit_ignore=exit_ignore) |
|
|||
614 |
|
|
717 | t1 = clock2() | |
615 |
|
|
718 | t_usr = t1[0] - t0[0] | |
616 |
|
|
719 | t_sys = t1[1] - t0[1] | |
@@ -621,8 +724,7 b' python-profiler package from non-free.""")' | |||||
621 |
|
|
724 | runs = range(nruns) | |
622 |
|
|
725 | t0 = clock2() | |
623 |
|
|
726 | for nr in runs: | |
624 | runner(filename, prog_ns, prog_ns, |
|
727 | run() | |
625 | exit_ignore=exit_ignore) |
|
|||
626 |
|
|
728 | t1 = clock2() | |
627 |
|
|
729 | t_usr = t1[0] - t0[0] | |
628 |
|
|
730 | t_sys = t1[1] - t0[1] | |
@@ -634,49 +736,6 b' python-profiler package from non-free.""")' | |||||
634 |
|
|
736 | twall1 = time.time() | |
635 |
|
|
737 | print "Wall time: %10.2f s." % (twall1 - twall0) | |
636 |
|
738 | |||
637 | else: |
|
|||
638 | # regular execution |
|
|||
639 | runner(filename, prog_ns, prog_ns, exit_ignore=exit_ignore) |
|
|||
640 |
|
||||
641 | if 'i' in opts: |
|
|||
642 | self.shell.user_ns['__name__'] = __name__save |
|
|||
643 | else: |
|
|||
644 | # The shell MUST hold a reference to prog_ns so after %run |
|
|||
645 | # exits, the python deletion mechanism doesn't zero it out |
|
|||
646 | # (leaving dangling references). |
|
|||
647 | self.shell.cache_main_mod(prog_ns, filename) |
|
|||
648 | # update IPython interactive namespace |
|
|||
649 |
|
||||
650 | # Some forms of read errors on the file may mean the |
|
|||
651 | # __name__ key was never set; using pop we don't have to |
|
|||
652 | # worry about a possible KeyError. |
|
|||
653 | prog_ns.pop('__name__', None) |
|
|||
654 |
|
||||
655 | with preserve_keys(self.shell.user_ns, '__file__'): |
|
|||
656 | self.shell.user_ns.update(prog_ns) |
|
|||
657 | finally: |
|
|||
658 | # It's a bit of a mystery why, but __builtins__ can change from |
|
|||
659 | # being a module to becoming a dict missing some key data after |
|
|||
660 | # %run. As best I can see, this is NOT something IPython is doing |
|
|||
661 | # at all, and similar problems have been reported before: |
|
|||
662 | # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html |
|
|||
663 | # Since this seems to be done by the interpreter itself, the best |
|
|||
664 | # we can do is to at least restore __builtins__ for the user on |
|
|||
665 | # exit. |
|
|||
666 | self.shell.user_ns['__builtins__'] = builtin_mod |
|
|||
667 |
|
||||
668 | # Ensure key global structures are restored |
|
|||
669 | sys.argv = save_argv |
|
|||
670 | if restore_main: |
|
|||
671 | sys.modules['__main__'] = restore_main |
|
|||
672 | else: |
|
|||
673 | # Remove from sys.modules the reference to main_mod we'd |
|
|||
674 | # added. Otherwise it will trap references to objects |
|
|||
675 | # contained therein. |
|
|||
676 | del sys.modules[main_mod_name] |
|
|||
677 |
|
||||
678 | return stats |
|
|||
679 |
|
||||
680 | @skip_doctest |
|
739 | @skip_doctest | |
681 | @line_cell_magic |
|
740 | @line_cell_magic | |
682 | def timeit(self, line='', cell=None): |
|
741 | def timeit(self, line='', cell=None): |
@@ -13,9 +13,13 b' from __future__ import absolute_import' | |||||
13 | # Imports |
|
13 | # Imports | |
14 | #----------------------------------------------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
15 |
|
15 | |||
|
16 | import functools | |||
16 | import os |
|
17 | import os | |
|
18 | import random | |||
17 | import sys |
|
19 | import sys | |
18 | import tempfile |
|
20 | import tempfile | |
|
21 | import textwrap | |||
|
22 | import unittest | |||
19 |
|
23 | |||
20 | import nose.tools as nt |
|
24 | import nose.tools as nt | |
21 | from nose import SkipTest |
|
25 | from nose import SkipTest | |
@@ -23,6 +27,8 b' from nose import SkipTest' | |||||
23 | from IPython.testing import decorators as dec |
|
27 | from IPython.testing import decorators as dec | |
24 | from IPython.testing import tools as tt |
|
28 | from IPython.testing import tools as tt | |
25 | from IPython.utils import py3compat |
|
29 | from IPython.utils import py3compat | |
|
30 | from IPython.utils.tempdir import TemporaryDirectory | |||
|
31 | from IPython.core import debugger | |||
26 |
|
32 | |||
27 | #----------------------------------------------------------------------------- |
|
33 | #----------------------------------------------------------------------------- | |
28 | # Test functions begin |
|
34 | # Test functions begin | |
@@ -337,3 +343,74 b' tclass.py: deleting object: C-third' | |||||
337 | self.mktmp(src) |
|
343 | self.mktmp(src) | |
338 | _ip.magic('run -t -N 1 %s' % self.fname) |
|
344 | _ip.magic('run -t -N 1 %s' % self.fname) | |
339 | _ip.magic('run -t -N 10 %s' % self.fname) |
|
345 | _ip.magic('run -t -N 10 %s' % self.fname) | |
|
346 | ||||
|
347 | ||||
|
348 | class TestMagicRunWithPackage(unittest.TestCase): | |||
|
349 | ||||
|
350 | def writefile(self, name, content): | |||
|
351 | path = os.path.join(self.tempdir.name, name) | |||
|
352 | d = os.path.dirname(path) | |||
|
353 | if not os.path.isdir(d): | |||
|
354 | os.makedirs(d) | |||
|
355 | with open(path, 'w') as f: | |||
|
356 | f.write(textwrap.dedent(content)) | |||
|
357 | ||||
|
358 | def setUp(self): | |||
|
359 | self.package = package = 'tmp{0}'.format(repr(random.random())[2:]) | |||
|
360 | """Temporary valid python package name.""" | |||
|
361 | ||||
|
362 | self.value = int(random.random() * 10000) | |||
|
363 | ||||
|
364 | self.tempdir = TemporaryDirectory() | |||
|
365 | self.__orig_cwd = os.getcwdu() | |||
|
366 | sys.path.insert(0, self.tempdir.name) | |||
|
367 | ||||
|
368 | self.writefile(os.path.join(package, '__init__.py'), '') | |||
|
369 | self.writefile(os.path.join(package, 'sub.py'), """ | |||
|
370 | x = {0!r} | |||
|
371 | """.format(self.value)) | |||
|
372 | self.writefile(os.path.join(package, 'relative.py'), """ | |||
|
373 | from .sub import x | |||
|
374 | """) | |||
|
375 | self.writefile(os.path.join(package, 'absolute.py'), """ | |||
|
376 | from {0}.sub import x | |||
|
377 | """.format(package)) | |||
|
378 | ||||
|
379 | def tearDown(self): | |||
|
380 | os.chdir(self.__orig_cwd) | |||
|
381 | sys.path[:] = [p for p in sys.path if p != self.tempdir.name] | |||
|
382 | self.tempdir.cleanup() | |||
|
383 | ||||
|
384 | def check_run_submodule(self, submodule, opts=''): | |||
|
385 | _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts)) | |||
|
386 | self.assertEqual(_ip.user_ns['x'], self.value, | |||
|
387 | 'Variable `x` is not loaded from module `{0}`.' | |||
|
388 | .format(submodule)) | |||
|
389 | ||||
|
390 | def test_run_submodule_with_absolute_import(self): | |||
|
391 | self.check_run_submodule('absolute') | |||
|
392 | ||||
|
393 | def test_run_submodule_with_relative_import(self): | |||
|
394 | """Run submodule that has a relative import statement (#2727).""" | |||
|
395 | self.check_run_submodule('relative') | |||
|
396 | ||||
|
397 | def test_prun_submodule_with_absolute_import(self): | |||
|
398 | self.check_run_submodule('absolute', '-p') | |||
|
399 | ||||
|
400 | def test_prun_submodule_with_relative_import(self): | |||
|
401 | self.check_run_submodule('relative', '-p') | |||
|
402 | ||||
|
403 | def with_fake_debugger(func): | |||
|
404 | @functools.wraps(func) | |||
|
405 | def wrapper(*args, **kwds): | |||
|
406 | with tt.monkeypatch(debugger.Pdb, 'run', staticmethod(eval)): | |||
|
407 | return func(*args, **kwds) | |||
|
408 | return wrapper | |||
|
409 | ||||
|
410 | @with_fake_debugger | |||
|
411 | def test_debug_run_submodule_with_absolute_import(self): | |||
|
412 | self.check_run_submodule('absolute', '-d') | |||
|
413 | ||||
|
414 | @with_fake_debugger | |||
|
415 | def test_debug_run_submodule_with_relative_import(self): | |||
|
416 | self.check_run_submodule('relative', '-d') |
General Comments 0
You need to be logged in to leave comments.
Login now