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 | 445 | Parameters |
|
446 | 446 | ---------- |
|
447 | 447 | rangestr : str |
|
448 |
A string specifying ranges, e.g. "5 ~2/1-4". |
|
|
449 | :func:`magic_history` for full details. | |
|
448 | A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used, | |
|
449 | this will return everything from current session's history. | |
|
450 | ||
|
451 | See the documentation of :func:`%history` for the full details. | |
|
452 | ||
|
450 | 453 | raw, output : bool |
|
451 | 454 | As :meth:`get_range` |
|
452 | 455 | |
@@ -851,11 +854,18 b' $""", re.VERBOSE)' | |||
|
851 | 854 | def extract_hist_ranges(ranges_str): |
|
852 | 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 | 860 | Examples |
|
855 | 861 | -------- |
|
856 | 862 | >>> list(extract_hist_ranges("~8/5-~7/4 2")) |
|
857 | 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 | 869 | for range_str in ranges_str.split(): |
|
860 | 870 | rmatch = range_re.match(range_str) |
|
861 | 871 | if not rmatch: |
@@ -3696,12 +3696,15 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3696 | 3696 | |
|
3697 | 3697 | Parameters |
|
3698 | 3698 | ---------- |
|
3699 |
range_str : str |
|
|
3699 | range_str : str | |
|
3700 | 3700 | The set of slices is given as a string, like "~5/6-~4/2 4:8 9", |
|
3701 | 3701 | since this function is for use by magic functions which get their |
|
3702 | 3702 | arguments as strings. The number before the / is the session |
|
3703 | 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 | 3708 | raw : bool, optional |
|
3706 | 3709 | By default, the processed input is used. If this is true, the raw |
|
3707 | 3710 | input history is used instead. |
@@ -3715,7 +3718,16 b' class InteractiveShell(SingletonConfigurable):' | |||
|
3715 | 3718 | * ``N-M`` -> include items N..M (closed endpoint). |
|
3716 | 3719 | """ |
|
3717 | 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 | 3732 | def find_user_code(self, target, raw=True, py_only=False, skip_encoding_cookie=True, search_ns=False): |
|
3721 | 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 | 3737 | Parameters |
|
3726 | 3738 | ---------- |
|
3727 | ||
|
3728 | 3739 | target : str |
|
3729 | ||
|
3730 | 3740 | A string specifying code to retrieve. This will be tried respectively |
|
3731 | 3741 | as: ranges of input history (see %history for syntax), url, |
|
3732 | 3742 | corresponding .py file, filename, or an expression evaluating to a |
|
3733 | 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 | 3748 | raw : bool |
|
3736 | 3749 | If true (default), retrieve raw history. Has no effect on the other |
|
3737 | 3750 | retrieval mechanisms. |
@@ -202,6 +202,9 b' class CodeMagics(Magics):' | |||
|
202 | 202 | This function uses the same syntax as %history for input ranges, |
|
203 | 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 | 208 | It adds a '.py' extension to the file if you don't do so yourself, and |
|
206 | 209 | it asks for confirmation before overwriting existing files. |
|
207 | 210 | |
@@ -254,6 +257,9 b' class CodeMagics(Magics):' | |||
|
254 | 257 | The argument can be an input history range, a filename, or the name of a |
|
255 | 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 | 263 | Options: |
|
258 | 264 | |
|
259 | 265 | -d: Pass a custom description. The default will say |
@@ -315,6 +321,9 b' class CodeMagics(Magics):' | |||
|
315 | 321 | where source can be a filename, URL, input history range, macro, or |
|
316 | 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 | 327 | Options: |
|
319 | 328 | |
|
320 | 329 | -r <lines>: Specify lines or ranges of lines to load from the source. |
@@ -333,6 +342,7 b' class CodeMagics(Magics):' | |||
|
333 | 342 | confirmation before loading source with more than 200 000 characters, unless |
|
334 | 343 | -y flag is passed or if the frontend does not support raw_input:: |
|
335 | 344 | |
|
345 | %load | |
|
336 | 346 | %load myscript.py |
|
337 | 347 | %load 7-27 |
|
338 | 348 | %load myMacro |
@@ -344,13 +354,7 b' class CodeMagics(Magics):' | |||
|
344 | 354 | %load -n my_module.wonder_function |
|
345 | 355 | """ |
|
346 | 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 | 357 | search_ns = 'n' in opts |
|
353 | ||
|
354 | 358 | contents = self.shell.find_user_code(args, search_ns=search_ns) |
|
355 | 359 | |
|
356 | 360 | if 's' in opts: |
@@ -184,14 +184,12 b' class HistoryMagics(Magics):' | |||
|
184 | 184 | n = 10 if limit is None else limit |
|
185 | 185 | hist = history_manager.get_tail(n, raw=raw, output=get_output) |
|
186 | 186 | else: |
|
187 | if args.range: # Get history by ranges | |
|
188 | if args.pattern: | |
|
189 | range_pattern = "*" + " ".join(args.pattern) + "*" | |
|
190 | print_nums = True | |
|
191 | hist = history_manager.get_range_by_str(" ".join(args.range), | |
|
192 | raw, get_output) | |
|
193 | else: # Just get history for the current session | |
|
194 | hist = history_manager.get_range(raw=raw, output=get_output) | |
|
187 | if args.pattern: | |
|
188 | range_pattern = "*" + " ".join(args.pattern) + "*" | |
|
189 | print_nums = True | |
|
190 | hist = history_manager.get_range_by_str( | |
|
191 | " ".join(args.range), raw, get_output | |
|
192 | ) | |
|
195 | 193 | |
|
196 | 194 | # We could be displaying the entire history, so let's not try to pull |
|
197 | 195 | # it into a list in memory. Anything that needs more space will just |
@@ -806,18 +806,17 b' class OSMagics(Magics):' | |||
|
806 | 806 | to be Python source and will show it with syntax highlighting. |
|
807 | 807 | |
|
808 | 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 | 814 | %pycat myscript.py |
|
812 | 815 | %pycat 7-27 |
|
813 | 816 | %pycat myMacro |
|
814 | 817 | %pycat http://www.example.com/myscript.py |
|
815 | 818 | """ |
|
816 | if not parameter_s: | |
|
817 | raise UsageError('Missing filename, URL, input history range, ' | |
|
818 | 'or macro.') | |
|
819 | ||
|
820 | try : | |
|
819 | try: | |
|
821 | 820 | cont = self.shell.find_user_code(parameter_s, skip_encoding_cookie=False) |
|
822 | 821 | except (ValueError, IOError): |
|
823 | 822 | print("Error: no such file, variable, URL, history range or macro") |
@@ -160,6 +160,14 b' def test_extract_hist_ranges():' | |||
|
160 | 160 | actual = list(extract_hist_ranges(instr)) |
|
161 | 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 | 171 | def test_magic_rerun(): |
|
164 | 172 | """Simple test for %rerun (no args -> rerun last line)""" |
|
165 | 173 | ip = get_ipython() |
@@ -1089,6 +1089,29 b' def test_save():' | |||
|
1089 | 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 | 1115 | def test_store(): |
|
1093 | 1116 | """Test %store.""" |
|
1094 | 1117 | ip = get_ipython() |
General Comments 0
You need to be logged in to leave comments.
Login now