##// END OF EJS Templates
Replace `%averbose` with args to `%autoreload`
Emilio Graff -
Show More
@@ -53,6 +53,10 b' The following magic commands are provided:'
53 Same as 2/all, but also adds any new objects in the module. See
53 Same as 2/all, but also adds any new objects in the module. See
54 unit test at IPython/extensions/tests/test_autoreload.py::test_autoload_newly_added_objects
54 unit test at IPython/extensions/tests/test_autoreload.py::test_autoload_newly_added_objects
55
55
56 Adding ``--print`` or ``-p`` to the ``%autoreload`` line will print autoreload activity to
57 standard out. ``--log`` or ``-l`` will do it to the log at INFO level; both can be used
58 simultaneously.
59
56 ``%aimport``
60 ``%aimport``
57
61
58 List modules which are to be automatically imported or not to be imported.
62 List modules which are to be automatically imported or not to be imported.
@@ -69,18 +73,6 b' The following magic commands are provided:'
69
73
70 Mark module 'foo' to not be autoreloaded.
74 Mark module 'foo' to not be autoreloaded.
71
75
72 ``%averbose off``
73
74 Perform autoreload tasks quietly
75
76 ``%averbose on``
77
78 Report activity with `print` statements.
79
80 ``%averbose log``
81
82 Report activity with the logger.
83
84 Caveats
76 Caveats
85 =======
77 =======
86
78
@@ -113,6 +105,7 b' Some of the known remaining caveats are:'
113 - Reloading a module, or importing the same module by a different name, creates new Enums. These may look the same, but are not.
105 - Reloading a module, or importing the same module by a different name, creates new Enums. These may look the same, but are not.
114 """
106 """
115
107
108 from IPython.core import magic_arguments
116 from IPython.core.magic import Magics, magics_class, line_magic
109 from IPython.core.magic import Magics, magics_class, line_magic
117
110
118 __skip_doctest__ = True
111 __skip_doctest__ = True
@@ -525,7 +518,28 b' class AutoreloadMagics(Magics):'
525 self.loaded_modules = set(sys.modules)
518 self.loaded_modules = set(sys.modules)
526
519
527 @line_magic
520 @line_magic
528 def autoreload(self, parameter_s=""):
521 @magic_arguments.magic_arguments()
522 @magic_arguments.argument('mode', type=str, default='now', nargs='?',
523 help="""
524 blank or 'now' - Reload all modules (except those excluded by
525 %%aimport) automaticallynow.
526
527 '0' or 'off' - Disable automatic reloading.
528
529 '1' or 'explicit' - Reload only modules imported with %%aimport every
530 time before executing the Python code typed.
531
532 '2' or 'all' - Reload all modules (except those excluded by %%aimport)
533 every time before executing the Python code typed.
534
535 '3' or 'complete' - Same as 2/all, but also but also adds any new
536 objects in the module.
537 """)
538 @magic_arguments.argument('-p', '--print', action='store_true', default=False,
539 help='Show autoreload activity using `print` statements')
540 @magic_arguments.argument('-l', '--log', action='store_true', default=False,
541 help='Show autoreload activity using the logger')
542 def autoreload(self, line=""):
529 r"""%autoreload => Reload modules automatically
543 r"""%autoreload => Reload modules automatically
530
544
531 %autoreload or %autoreload now
545 %autoreload or %autoreload now
@@ -547,6 +561,10 b' class AutoreloadMagics(Magics):'
547 Same as 2/all, but also but also adds any new objects in the module. See
561 Same as 2/all, but also but also adds any new objects in the module. See
548 unit test at IPython/extensions/tests/test_autoreload.py::test_autoload_newly_added_objects
562 unit test at IPython/extensions/tests/test_autoreload.py::test_autoload_newly_added_objects
549
563
564 The optional arguments --print and --log control display of autoreload activity. The default
565 is to act silently; --print (or -p) will print out the names of modules that are being
566 reloaded, and --log (or -l) outputs them to the log at INFO level.
567
550 Reloading Python modules in a reliable way is in general
568 Reloading Python modules in a reliable way is in general
551 difficult, and unexpected things may occur. %autoreload tries to
569 difficult, and unexpected things may occur. %autoreload tries to
552 work around common pitfalls by replacing function code objects and
570 work around common pitfalls by replacing function code objects and
@@ -573,23 +591,45 b' class AutoreloadMagics(Magics):'
573 autoreloaded.
591 autoreloaded.
574
592
575 """
593 """
576 parameter_s_lower = parameter_s.lower()
594 args = magic_arguments.parse_argstring(self.autoreload, line)
577 if parameter_s == "" or parameter_s_lower == "now":
595 mode = args.mode.lower()
596
597 def p(msg):
598 print(msg)
599
600 def l(msg):
601 logging.getLogger("autoreload").info(msg)
602
603 def pl(msg):
604 p(msg)
605 l(msg)
606
607 if args.print is False and args.log is False:
608 self._reloader._report = lambda msg: None
609 elif args.print is True:
610 if args.log is True:
611 self._reloader._report = lambda msg: pl(msg)
612 else:
613 self._reloader._report = lambda msg: p(msg)
614 elif args.log is True:
615 self._reloader._report = lambda msg: l(msg)
616
617 if mode == "" or mode == "now":
578 self._reloader.check(True)
618 self._reloader.check(True)
579 elif parameter_s == "0" or parameter_s_lower == "off":
619 elif mode == "0" or mode == "off":
580 self._reloader.enabled = False
620 self._reloader.enabled = False
581 elif parameter_s == "1" or parameter_s_lower == "explicit":
621 elif mode == "1" or mode == "explicit":
582 self._reloader.check_all = False
622 self._reloader.check_all = False
583 self._reloader.enabled = True
623 self._reloader.enabled = True
584 elif parameter_s == "2" or parameter_s_lower == "all":
624 elif mode == "2" or mode == "all":
585 self._reloader.check_all = True
625 self._reloader.check_all = True
586 self._reloader.enabled = True
626 self._reloader.enabled = True
587 elif parameter_s == "3" or parameter_s_lower == "complete":
627 elif mode == "3" or mode == "complete":
588 self._reloader.check_all = True
628 self._reloader.check_all = True
589 self._reloader.enabled = True
629 self._reloader.enabled = True
590 self._reloader.autoload_obj = True
630 self._reloader.autoload_obj = True
591 else:
631 else:
592 raise ValueError(f'Unrecognized parameter "{parameter_s}".')
632 raise ValueError(f'Unrecognized autoreload mode "{mode}".')
593
633
594 @line_magic
634 @line_magic
595 def aimport(self, parameter_s="", stream=None):
635 def aimport(self, parameter_s="", stream=None):
@@ -630,31 +670,6 b' class AutoreloadMagics(Magics):'
630 # Inject module to user namespace
670 # Inject module to user namespace
631 self.shell.push({top_name: top_module})
671 self.shell.push({top_name: top_module})
632
672
633 @line_magic
634 def averbose(self, parameter_s=""):
635 r"""%averbose => Turn verbosity on/off for autoreloading.
636
637 %averbose 0 or %averbose off
638 Turn off any reporting during autoreload.
639
640 %averbose 1 or %averbose on
641 Report autoreload activity via print statements.
642
643 %averbose 2 or %averbose log
644 Report autoreload activity via logging.
645 """
646
647 if parameter_s == "0" or parameter_s.lower() == "off":
648 self._reloader._report = lambda msg: None
649 elif parameter_s == "1" or parameter_s.lower() == "on":
650 self._reloader._report = lambda msg: print(msg)
651 elif parameter_s == "2" or parameter_s.lower() == "log":
652 self._reloader._report = lambda msg: logging.getLogger("autoreload").info(
653 msg
654 )
655 else:
656 raise ValueError(f'Unrecognized parameter "{parameter_s}".')
657
658 def pre_run_cell(self):
673 def pre_run_cell(self):
659 if self._reloader.enabled:
674 if self._reloader.enabled:
660 try:
675 try:
@@ -71,9 +71,6 b' class FakeShell:'
71 self.auto_magics.aimport(parameter, stream=stream)
71 self.auto_magics.aimport(parameter, stream=stream)
72 self.auto_magics.post_execute_hook()
72 self.auto_magics.post_execute_hook()
73
73
74 def magic_averbose(self, parameter):
75 self.auto_magics.averbose(parameter)
76
77 class Fixture(TestCase):
74 class Fixture(TestCase):
78 """Fixture for creating test module files"""
75 """Fixture for creating test module files"""
79
76
@@ -442,41 +439,40 b' class TestAutoreload(Fixture):'
442 assert module_reloader.skip_modules["os"] is True
439 assert module_reloader.skip_modules["os"] is True
443 assert "os" not in module_reloader.modules.keys()
440 assert "os" not in module_reloader.modules.keys()
444
441
445 def test_averbose(self):
442 def test_autoreload_output(self):
446 self.shell.magic_averbose("off")
447 self.shell.magic_autoreload("complete")
443 self.shell.magic_autoreload("complete")
448 mod_code = """
444 mod_code = """
449 def func1(): pass
445 def func1(): pass
450 """
446 """
451 mod_name, mod_fn = self.new_module(mod_code)
447 mod_name, mod_fn = self.new_module(mod_code)
452 self.shell.run_code(f"import {mod_name}")
448 self.shell.run_code(f"import {mod_name}")
453 with tt.AssertPrints("", channel="stdout"): # no output.
449 with tt.AssertPrints("", channel="stdout"): # no output; this is default
454 self.shell.run_code("pass")
450 self.shell.run_code("pass")
455
451
452 self.shell.magic_autoreload("complete --print")
456 self.write_file(mod_fn, mod_code) # "modify" the module
453 self.write_file(mod_fn, mod_code) # "modify" the module
457 self.shell.magic_averbose("on") # Should now see a print statement.
454 with tt.AssertPrints(f"Reloading '{mod_name}'.", channel="stdout"): # see something printed out
458 with tt.AssertPrints(f"Reloading '{mod_name}'.", channel="stdout"):
459 self.shell.run_code("pass")
455 self.shell.run_code("pass")
460
456
457 self.shell.magic_autoreload("complete -p")
461 self.write_file(mod_fn, mod_code) # "modify" the module
458 self.write_file(mod_fn, mod_code) # "modify" the module
462 self.shell.magic_averbose("off") # Should not see anything on next call
459 with tt.AssertPrints(f"Reloading '{mod_name}'.", channel="stdout"): # see something printed out
463 with tt.AssertPrints("", channel="stdout"):
464 self.shell.run_code("pass")
460 self.shell.run_code("pass")
465
461
466 # TODO: test logging. Why won't this work?
462 self.shell.magic_autoreload("complete --print --log")
467 # with tt.AssertPrints('LOGGER: '):
463 self.write_file(mod_fn, mod_code) # "modify" the module
468 # self.shell.run_code("import logging; logging.basicConfig(format='LOGGER: %(message)s');"
464 with tt.AssertPrints(f"Reloading '{mod_name}'.", channel="stdout"): # see something printed out
469 # "logger = logging.getLogger(); logger.setLevel(logging.DEBUG);"
465 self.shell.run_code("pass")
470 # "logger.info('test')")
471
472 # self.shell.magic_averbose("log") # Should see it formatted as per our logging config
473 # self.write_file(mod_fn, mod_code) # "modify" the module
474 # with tt.AssertPrints(f"LOGGER: Reloading '{mod_name}'.", channel="stdout"):
475 # self.shell.run_code("pass")
476
466
477 # And an invalid mode name raises an exception.
467 # TODO: test logging, i.e. --log. Why won't this work?
478 with self.assertRaises(ValueError):
468 # with tt.AssertPrints("LOGGER: test", channel="stdout"):
479 self.shell.magic_averbose("fax")
469 # # logger.info('test')
470 # self.shell.run_code("import logging; import sys;"
471 # "logging.basicConfig(format='LOGGER: %(message)s',"
472 # " stream=sys.stdout,"
473 # " level=logging.DEBUG);"
474 # "logging.getLogger().info('test');"
475 # )
480
476
481 def _check_smoketest(self, use_aimport=True):
477 def _check_smoketest(self, use_aimport=True):
482 """
478 """
@@ -10,12 +10,12 b' We introduce more descriptive names for the `%autoreload` parameter:'
10
10
11 The original designations (e.g. "2") still work, and these new ones are case-insensitive.
11 The original designations (e.g. "2") still work, and these new ones are case-insensitive.
12
12
13 Additionally, the option `--print` or `-p` can be added to the line to print the names of modules
14 being reloaded. Similarly, `--log` or `-l` will output the names to the logger at INFO level. Both
15 can be used simultaneously.
16
13 The parsing logic for `%aimport` is now improved such that modules can be whitelisted and
17 The parsing logic for `%aimport` is now improved such that modules can be whitelisted and
14 blacklisted in the same line, e.g. it's now possible to call `%aimport os, -math` to include `os`
18 blacklisted in the same line, e.g. it's now possible to call `%aimport os, -math` to include `os`
15 for `%autoreload explicit` and exclude `math` for modes 2 and 3.
19 for `%autoreload explicit` and exclude `math` for modes 2 and 3.
16
20
17 A new magic command `%averbose` controls printing of the names of modules about to be autoreloaded.
18 - `%averbose off` / `%averbose 0` - turns off all output (default behavior)
19 - `%averbose on` / `%averbose 1` - uses `print` to display module name
20 - `%averbose log` / `%averbose 2` - logs an `INFO` message with the module name
21
21
General Comments 0
You need to be logged in to leave comments. Login now