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 |
|
619 | elif mode == "0" or mode == "off": | |
580 | self._reloader.enabled = False |
|
620 | self._reloader.enabled = False | |
581 |
elif |
|
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 |
|
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 |
|
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 |
|
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_a |
|
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