Show More
@@ -0,0 +1,19 b'' | |||||
|
1 | Empty History Ranges | |||
|
2 | ==================== | |||
|
3 | ||||
|
4 | A number of magics that take history ranges can now be used with an empty | |||
|
5 | range. These magics are: | |||
|
6 | ||||
|
7 | * ``%save`` | |||
|
8 | * ``%load`` | |||
|
9 | * ``%pastebin`` | |||
|
10 | * ``%pycat`` | |||
|
11 | ||||
|
12 | Using them this way will make them take the history of the current session up | |||
|
13 | to the point of the magic call (such that the magic itself will not be | |||
|
14 | included). | |||
|
15 | ||||
|
16 | Therefore it is now possible to save the whole history to a file using simple | |||
|
17 | ``%save <filename>``, load and edit it using ``%load`` (makes for a nice usage | |||
|
18 | when followed with :kbd:`F2`), send it to dpaste.org using ``%pastebin``, or | |||
|
19 | view the whole thing syntax-highlighted with a single ``%pycat``. |
@@ -445,8 +445,11 b' class HistoryAccessor(HistoryAccessorBase):' | |||||
445 | Parameters |
|
445 | Parameters | |
446 | ---------- |
|
446 | ---------- | |
447 | rangestr : str |
|
447 | rangestr : str | |
448 |
A string specifying ranges, e.g. "5 ~2/1-4". |
|
448 | A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used, | |
449 | :func:`magic_history` for full details. |
|
449 | this will return everything from current session's history. | |
|
450 | ||||
|
451 | See the documentation of :func:`%history` for the full details. | |||
|
452 | ||||
450 | raw, output : bool |
|
453 | raw, output : bool | |
451 | As :meth:`get_range` |
|
454 | As :meth:`get_range` | |
452 |
|
455 | |||
@@ -851,11 +854,18 b' $""", re.VERBOSE)' | |||||
851 | def extract_hist_ranges(ranges_str): |
|
854 | def extract_hist_ranges(ranges_str): | |
852 | """Turn a string of history ranges into 3-tuples of (session, start, stop). |
|
855 | """Turn a string of history ranges into 3-tuples of (session, start, stop). | |
853 |
|
856 | |||
|
857 | Empty string results in a `[(0, 1, None)]`, i.e. "everything from current | |||
|
858 | session". | |||
|
859 | ||||
854 | Examples |
|
860 | Examples | |
855 | -------- |
|
861 | -------- | |
856 | >>> list(extract_hist_ranges("~8/5-~7/4 2")) |
|
862 | >>> list(extract_hist_ranges("~8/5-~7/4 2")) | |
857 | [(-8, 5, None), (-7, 1, 5), (0, 2, 3)] |
|
863 | [(-8, 5, None), (-7, 1, 5), (0, 2, 3)] | |
858 | """ |
|
864 | """ | |
|
865 | if ranges_str == "": | |||
|
866 | yield (0, 1, None) # Everything from current session | |||
|
867 | return | |||
|
868 | ||||
859 | for range_str in ranges_str.split(): |
|
869 | for range_str in ranges_str.split(): | |
860 | rmatch = range_re.match(range_str) |
|
870 | rmatch = range_re.match(range_str) | |
861 | if not rmatch: |
|
871 | if not rmatch: |
@@ -3696,12 +3696,15 b' class InteractiveShell(SingletonConfigurable):' | |||||
3696 |
|
3696 | |||
3697 | Parameters |
|
3697 | Parameters | |
3698 | ---------- |
|
3698 | ---------- | |
3699 |
range_str : str |
|
3699 | range_str : str | |
3700 | The set of slices is given as a string, like "~5/6-~4/2 4:8 9", |
|
3700 | The set of slices is given as a string, like "~5/6-~4/2 4:8 9", | |
3701 | since this function is for use by magic functions which get their |
|
3701 | since this function is for use by magic functions which get their | |
3702 | arguments as strings. The number before the / is the session |
|
3702 | arguments as strings. The number before the / is the session | |
3703 | number: ~n goes n back from the current session. |
|
3703 | number: ~n goes n back from the current session. | |
3704 |
|
3704 | |||
|
3705 | If empty string is given, returns history of current session | |||
|
3706 | without the last input. | |||
|
3707 | ||||
3705 | raw : bool, optional |
|
3708 | raw : bool, optional | |
3706 | By default, the processed input is used. If this is true, the raw |
|
3709 | By default, the processed input is used. If this is true, the raw | |
3707 | input history is used instead. |
|
3710 | input history is used instead. | |
@@ -3715,7 +3718,16 b' class InteractiveShell(SingletonConfigurable):' | |||||
3715 | * ``N-M`` -> include items N..M (closed endpoint). |
|
3718 | * ``N-M`` -> include items N..M (closed endpoint). | |
3716 | """ |
|
3719 | """ | |
3717 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) |
|
3720 | lines = self.history_manager.get_range_by_str(range_str, raw=raw) | |
3718 |
|
|
3721 | text = "\n".join(x for _, _, x in lines) | |
|
3722 | ||||
|
3723 | # Skip the last line, as it's probably the magic that called this | |||
|
3724 | if not range_str: | |||
|
3725 | if "\n" not in text: | |||
|
3726 | text = "" | |||
|
3727 | else: | |||
|
3728 | text = text[: text.rfind("\n")] | |||
|
3729 | ||||
|
3730 | return text | |||
3719 |
|
3731 | |||
3720 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False): |
|
3732 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False): | |
3721 | """Get a code string from history, file, url, or a string or macro. |
|
3733 | """Get a code string from history, file, url, or a string or macro. | |
@@ -3724,14 +3736,15 b' class InteractiveShell(SingletonConfigurable):' | |||||
3724 |
|
3736 | |||
3725 | Parameters |
|
3737 | Parameters | |
3726 | ---------- |
|
3738 | ---------- | |
3727 |
|
||||
3728 | target : str |
|
3739 | target : str | |
3729 |
|
||||
3730 | A string specifying code to retrieve. This will be tried respectively |
|
3740 | A string specifying code to retrieve. This will be tried respectively | |
3731 | as: ranges of input history (see %history for syntax), url, |
|
3741 | as: ranges of input history (see %history for syntax), url, | |
3732 | corresponding .py file, filename, or an expression evaluating to a |
|
3742 | corresponding .py file, filename, or an expression evaluating to a | |
3733 | string or Macro in the user namespace. |
|
3743 | string or Macro in the user namespace. | |
3734 |
|
3744 | |||
|
3745 | If empty string is given, returns complete history of current | |||
|
3746 | session, without the last line. | |||
|
3747 | ||||
3735 | raw : bool |
|
3748 | raw : bool | |
3736 | If true (default), retrieve raw history. Has no effect on the other |
|
3749 | If true (default), retrieve raw history. Has no effect on the other | |
3737 | retrieval mechanisms. |
|
3750 | retrieval mechanisms. |
@@ -202,6 +202,9 b' class CodeMagics(Magics):' | |||||
202 | This function uses the same syntax as %history for input ranges, |
|
202 | This function uses the same syntax as %history for input ranges, | |
203 | then saves the lines to the filename you specify. |
|
203 | then saves the lines to the filename you specify. | |
204 |
|
204 | |||
|
205 | If no ranges are specified, saves history of the current session up to | |||
|
206 | this point. | |||
|
207 | ||||
205 | It adds a '.py' extension to the file if you don't do so yourself, and |
|
208 | It adds a '.py' extension to the file if you don't do so yourself, and | |
206 | it asks for confirmation before overwriting existing files. |
|
209 | it asks for confirmation before overwriting existing files. | |
207 |
|
210 | |||
@@ -254,6 +257,9 b' class CodeMagics(Magics):' | |||||
254 | The argument can be an input history range, a filename, or the name of a |
|
257 | The argument can be an input history range, a filename, or the name of a | |
255 | string or macro. |
|
258 | string or macro. | |
256 |
|
259 | |||
|
260 | If no arguments are given, uploads the history of this session up to | |||
|
261 | this point. | |||
|
262 | ||||
257 | Options: |
|
263 | Options: | |
258 |
|
264 | |||
259 | -d: Pass a custom description. The default will say |
|
265 | -d: Pass a custom description. The default will say | |
@@ -315,6 +321,9 b' class CodeMagics(Magics):' | |||||
315 | where source can be a filename, URL, input history range, macro, or |
|
321 | where source can be a filename, URL, input history range, macro, or | |
316 | element in the user namespace |
|
322 | element in the user namespace | |
317 |
|
323 | |||
|
324 | If no arguments are given, loads the history of this session up to this | |||
|
325 | point. | |||
|
326 | ||||
318 | Options: |
|
327 | Options: | |
319 |
|
328 | |||
320 | -r <lines>: Specify lines or ranges of lines to load from the source. |
|
329 | -r <lines>: Specify lines or ranges of lines to load from the source. | |
@@ -333,6 +342,7 b' class CodeMagics(Magics):' | |||||
333 | confirmation before loading source with more than 200 000 characters, unless |
|
342 | confirmation before loading source with more than 200 000 characters, unless | |
334 | -y flag is passed or if the frontend does not support raw_input:: |
|
343 | -y flag is passed or if the frontend does not support raw_input:: | |
335 |
|
344 | |||
|
345 | %load | |||
336 | %load myscript.py |
|
346 | %load myscript.py | |
337 | %load 7-27 |
|
347 | %load 7-27 | |
338 | %load myMacro |
|
348 | %load myMacro | |
@@ -344,13 +354,7 b' class CodeMagics(Magics):' | |||||
344 | %load -n my_module.wonder_function |
|
354 | %load -n my_module.wonder_function | |
345 | """ |
|
355 | """ | |
346 | opts,args = self.parse_options(arg_s,'yns:r:') |
|
356 | opts,args = self.parse_options(arg_s,'yns:r:') | |
347 |
|
||||
348 | if not args: |
|
|||
349 | raise UsageError('Missing filename, URL, input history range, ' |
|
|||
350 | 'macro, or element in the user namespace.') |
|
|||
351 |
|
||||
352 | search_ns = 'n' in opts |
|
357 | search_ns = 'n' in opts | |
353 |
|
||||
354 | contents = self.shell.find_user_code(args, search_ns=search_ns) |
|
358 | contents = self.shell.find_user_code(args, search_ns=search_ns) | |
355 |
|
359 | |||
356 | if 's' in opts: |
|
360 | if 's' in opts: |
@@ -184,14 +184,12 b' class HistoryMagics(Magics):' | |||||
184 | n = 10 if limit is None else limit |
|
184 | n = 10 if limit is None else limit | |
185 | hist = history_manager.get_tail(n, raw=raw, output=get_output) |
|
185 | hist = history_manager.get_tail(n, raw=raw, output=get_output) | |
186 | else: |
|
186 | else: | |
187 | if args.range: # Get history by ranges |
|
187 | if args.pattern: | |
188 | if args.pattern: |
|
188 | range_pattern = "*" + " ".join(args.pattern) + "*" | |
189 | range_pattern = "*" + " ".join(args.pattern) + "*" |
|
189 | print_nums = True | |
190 | print_nums = True |
|
190 | hist = history_manager.get_range_by_str( | |
191 | hist = history_manager.get_range_by_str(" ".join(args.range), |
|
191 | " ".join(args.range), raw, get_output | |
192 | raw, get_output) |
|
192 | ) | |
193 | else: # Just get history for the current session |
|
|||
194 | hist = history_manager.get_range(raw=raw, output=get_output) |
|
|||
195 |
|
193 | |||
196 | # We could be displaying the entire history, so let's not try to pull |
|
194 | # We could be displaying the entire history, so let's not try to pull | |
197 | # it into a list in memory. Anything that needs more space will just |
|
195 | # it into a list in memory. Anything that needs more space will just |
@@ -806,18 +806,17 b' class OSMagics(Magics):' | |||||
806 | to be Python source and will show it with syntax highlighting. |
|
806 | to be Python source and will show it with syntax highlighting. | |
807 |
|
807 | |||
808 | This magic command can either take a local filename, an url, |
|
808 | This magic command can either take a local filename, an url, | |
809 |
an history range (see %history) or a macro as argument |
|
809 | an history range (see %history) or a macro as argument. | |
|
810 | ||||
|
811 | If no parameter is given, prints out history of current session up to | |||
|
812 | this point. :: | |||
810 |
|
813 | |||
811 | %pycat myscript.py |
|
814 | %pycat myscript.py | |
812 | %pycat 7-27 |
|
815 | %pycat 7-27 | |
813 | %pycat myMacro |
|
816 | %pycat myMacro | |
814 | %pycat http://www.example.com/myscript.py |
|
817 | %pycat http://www.example.com/myscript.py | |
815 | """ |
|
818 | """ | |
816 | if not parameter_s: |
|
819 | try: | |
817 | raise UsageError('Missing filename, URL, input history range, ' |
|
|||
818 | 'or macro.') |
|
|||
819 |
|
||||
820 | try : |
|
|||
821 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) |
|
820 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) | |
822 | except (ValueError, IOError): |
|
821 | except (ValueError, IOError): | |
823 | print("Error: no such file, variable, URL, history range or macro") |
|
822 | print("Error: no such file, variable, URL, history range or macro") |
@@ -160,6 +160,14 b' def test_extract_hist_ranges():' | |||||
160 | actual = list(extract_hist_ranges(instr)) |
|
160 | actual = list(extract_hist_ranges(instr)) | |
161 | nt.assert_equal(actual, expected) |
|
161 | nt.assert_equal(actual, expected) | |
162 |
|
162 | |||
|
163 | ||||
|
164 | def test_extract_hist_ranges_empty_str(): | |||
|
165 | instr = "" | |||
|
166 | expected = [(0, 1, None)] # 0 == current session, None == to end | |||
|
167 | actual = list(extract_hist_ranges(instr)) | |||
|
168 | nt.assert_equal(actual, expected) | |||
|
169 | ||||
|
170 | ||||
163 | def test_magic_rerun(): |
|
171 | def test_magic_rerun(): | |
164 | """Simple test for %rerun (no args -> rerun last line)""" |
|
172 | """Simple test for %rerun (no args -> rerun last line)""" | |
165 | ip = get_ipython() |
|
173 | ip = get_ipython() |
@@ -1089,6 +1089,29 b' def test_save():' | |||||
1089 | nt.assert_in("coding: utf-8", content) |
|
1089 | nt.assert_in("coding: utf-8", content) | |
1090 |
|
1090 | |||
1091 |
|
1091 | |||
|
1092 | def test_save_with_no_args(): | |||
|
1093 | ip = get_ipython() | |||
|
1094 | ip.history_manager.reset() # Clear any existing history. | |||
|
1095 | cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())", "%save"] | |||
|
1096 | for i, cmd in enumerate(cmds, start=1): | |||
|
1097 | ip.history_manager.store_inputs(i, cmd) | |||
|
1098 | ||||
|
1099 | with TemporaryDirectory() as tmpdir: | |||
|
1100 | path = os.path.join(tmpdir, "testsave.py") | |||
|
1101 | ip.run_line_magic("save", path) | |||
|
1102 | content = Path(path).read_text() | |||
|
1103 | expected_content = dedent( | |||
|
1104 | """\ | |||
|
1105 | # coding: utf-8 | |||
|
1106 | a=1 | |||
|
1107 | def b(): | |||
|
1108 | return a**2 | |||
|
1109 | print(a, b()) | |||
|
1110 | """ | |||
|
1111 | ) | |||
|
1112 | nt.assert_equal(content, expected_content) | |||
|
1113 | ||||
|
1114 | ||||
1092 | def test_store(): |
|
1115 | def test_store(): | |
1093 | """Test %store.""" |
|
1116 | """Test %store.""" | |
1094 | ip = get_ipython() |
|
1117 | ip = get_ipython() |
General Comments 0
You need to be logged in to leave comments.
Login now