##// END OF EJS Templates
Merge pull request #13049 from MrMino/empty_histrange_means_all...
Matthias Bussonnier -
r26671:be4887fe merge
parent child Browse files
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". See
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 : string
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 return "\n".join(x for _, _, x in lines)
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
188 if args.pattern:
187 if args.pattern:
189 range_pattern = "*" + " ".join(args.pattern) + "*"
188 range_pattern = "*" + " ".join(args.pattern) + "*"
190 print_nums = True
189 print_nums = True
191 hist = history_manager.get_range_by_str(" ".join(args.range),
190 hist = history_manager.get_range_by_str(
192 raw, get_output)
191 " ".join(args.range), raw, get_output
193 else: # Just get history for the current session
192 )
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,17 +806,16 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:
817 raise UsageError('Missing filename, URL, input history range, '
818 'or macro.')
819
820 try :
819 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):
@@ -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