Show More
@@ -202,7 +202,6 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
202 | 202 | config : :class:`~traitlets.config.loader.Config` |
|
203 | 203 | Config object. hist_file can also be set through this. |
|
204 | 204 | """ |
|
205 | # We need a pointer back to the shell for various tasks. | |
|
206 | 205 | super(HistoryAccessor, self).__init__(**traits) |
|
207 | 206 | # defer setting hist_file from kwarg until after init, |
|
208 | 207 | # otherwise the default kwarg value would clobber any value |
@@ -344,11 +343,6 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
344 | 343 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): |
|
345 | 344 | """Get the last n lines from the history database. |
|
346 | 345 | |
|
347 | Most recent entry last. | |
|
348 | ||
|
349 | Completion will be reordered so that that the last ones are when | |
|
350 | possible from current session. | |
|
351 | ||
|
352 | 346 | Parameters |
|
353 | 347 | ---------- |
|
354 | 348 | n : int |
@@ -367,31 +361,11 b' class HistoryAccessor(HistoryAccessorBase):' | |||
|
367 | 361 | self.writeout_cache() |
|
368 | 362 | if not include_latest: |
|
369 | 363 | n += 1 |
|
370 | # cursor/line/entry | |
|
371 | this_cur = list( | |
|
372 | self._run_sql( | |
|
373 | "WHERE session == ? ORDER BY line DESC LIMIT ? ", | |
|
374 | (self.session_number, n), | |
|
375 | raw=raw, | |
|
376 | output=output, | |
|
377 | ) | |
|
378 | ) | |
|
379 | other_cur = list( | |
|
380 | self._run_sql( | |
|
381 | "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?", | |
|
382 | (self.session_number, n), | |
|
383 | raw=raw, | |
|
384 | output=output, | |
|
385 | ) | |
|
386 | ) | |
|
387 | ||
|
388 | everything = this_cur + other_cur | |
|
389 | ||
|
390 | everything = everything[:n] | |
|
391 | ||
|
364 | cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?", | |
|
365 | (n,), raw=raw, output=output) | |
|
392 | 366 | if not include_latest: |
|
393 |
return list( |
|
|
394 |
return list( |
|
|
367 | return reversed(list(cur)[1:]) | |
|
368 | return reversed(list(cur)) | |
|
395 | 369 | |
|
396 | 370 | @catch_corrupt_db |
|
397 | 371 | def search(self, pattern="*", raw=True, search_raw=True, |
@@ -560,7 +534,6 b' class HistoryManager(HistoryAccessor):' | |||
|
560 | 534 | def __init__(self, shell=None, config=None, **traits): |
|
561 | 535 | """Create a new history manager associated with a shell instance. |
|
562 | 536 | """ |
|
563 | # We need a pointer back to the shell for various tasks. | |
|
564 | 537 | super(HistoryManager, self).__init__(shell=shell, config=config, |
|
565 | 538 | **traits) |
|
566 | 539 | self.save_flag = threading.Event() |
@@ -656,6 +629,59 b' class HistoryManager(HistoryAccessor):' | |||
|
656 | 629 | |
|
657 | 630 | return super(HistoryManager, self).get_session_info(session=session) |
|
658 | 631 | |
|
632 | @catch_corrupt_db | |
|
633 | def get_tail(self, n=10, raw=True, output=False, include_latest=False): | |
|
634 | """Get the last n lines from the history database. | |
|
635 | ||
|
636 | Most recent entry last. | |
|
637 | ||
|
638 | Completion will be reordered so that that the last ones are when | |
|
639 | possible from current session. | |
|
640 | ||
|
641 | Parameters | |
|
642 | ---------- | |
|
643 | n : int | |
|
644 | The number of lines to get | |
|
645 | raw, output : bool | |
|
646 | See :meth:`get_range` | |
|
647 | include_latest : bool | |
|
648 | If False (default), n+1 lines are fetched, and the latest one | |
|
649 | is discarded. This is intended to be used where the function | |
|
650 | is called by a user command, which it should not return. | |
|
651 | ||
|
652 | Returns | |
|
653 | ------- | |
|
654 | Tuples as :meth:`get_range` | |
|
655 | """ | |
|
656 | self.writeout_cache() | |
|
657 | if not include_latest: | |
|
658 | n += 1 | |
|
659 | # cursor/line/entry | |
|
660 | this_cur = list( | |
|
661 | self._run_sql( | |
|
662 | "WHERE session == ? ORDER BY line DESC LIMIT ? ", | |
|
663 | (self.session_number, n), | |
|
664 | raw=raw, | |
|
665 | output=output, | |
|
666 | ) | |
|
667 | ) | |
|
668 | other_cur = list( | |
|
669 | self._run_sql( | |
|
670 | "WHERE session != ? ORDER BY session DESC, line DESC LIMIT ?", | |
|
671 | (self.session_number, n), | |
|
672 | raw=raw, | |
|
673 | output=output, | |
|
674 | ) | |
|
675 | ) | |
|
676 | ||
|
677 | everything = this_cur + other_cur | |
|
678 | ||
|
679 | everything = everything[:n] | |
|
680 | ||
|
681 | if not include_latest: | |
|
682 | return list(everything)[:0:-1] | |
|
683 | return list(everything)[::-1] | |
|
684 | ||
|
659 | 685 | def _get_range_session(self, start=1, stop=None, raw=True, output=False): |
|
660 | 686 | """Get input and output history from the current session. Called by |
|
661 | 687 | get_range, and takes similar parameters.""" |
@@ -17,7 +17,7 b' from tempfile import TemporaryDirectory' | |||
|
17 | 17 | # our own packages |
|
18 | 18 | from traitlets.config.loader import Config |
|
19 | 19 | |
|
20 | from IPython.core.history import HistoryManager, extract_hist_ranges | |
|
20 | from IPython.core.history import HistoryAccessor, HistoryManager, extract_hist_ranges | |
|
21 | 21 | |
|
22 | 22 | |
|
23 | 23 | def test_proper_default_encoding(): |
@@ -227,3 +227,68 b' def test_histmanager_disabled():' | |||
|
227 | 227 | |
|
228 | 228 | # hist_file should not be created |
|
229 | 229 | assert hist_file.exists() is False |
|
230 | ||
|
231 | def test_get_tail_session_awareness(): | |
|
232 | """Test .get_tail() is: | |
|
233 | - session specific in HistoryManager | |
|
234 | - session agnostic in HistoryAccessor | |
|
235 | same for .get_last_session_id() | |
|
236 | """ | |
|
237 | ip = get_ipython() | |
|
238 | with TemporaryDirectory() as tmpdir: | |
|
239 | tmp_path = Path(tmpdir) | |
|
240 | hist_file = tmp_path / "history.sqlite" | |
|
241 | get_source = lambda x: x[2] | |
|
242 | ||
|
243 | # hm1 creates a new session and adds history entries, | |
|
244 | # ha catches up | |
|
245 | hm1 = HistoryManager(shell=ip, hist_file=hist_file) | |
|
246 | hm1_last_sid = hm1.get_last_session_id | |
|
247 | ha = HistoryAccessor(hist_file=hist_file) | |
|
248 | ha_last_sid = ha.get_last_session_id | |
|
249 | ||
|
250 | hist1 = ["a=1", "b=1", "c=1"] | |
|
251 | for i, h in enumerate(hist1 + [""], start=1): | |
|
252 | hm1.store_inputs(i, h) | |
|
253 | assert list(map(get_source, hm1.get_tail())) == hist1 | |
|
254 | assert list(map(get_source, ha.get_tail())) == hist1 | |
|
255 | sid1 = hm1_last_sid() | |
|
256 | assert sid1 is not None | |
|
257 | assert ha_last_sid() == sid1 | |
|
258 | ||
|
259 | # hm2 creates a new session and adds entries, | |
|
260 | # ha catches up | |
|
261 | hm2 = HistoryManager(shell=ip, hist_file=hist_file) | |
|
262 | hm2_last_sid = hm2.get_last_session_id | |
|
263 | ||
|
264 | hist2 = ["a=2", "b=2", "c=2"] | |
|
265 | for i, h in enumerate(hist2 + [""], start=1): | |
|
266 | hm2.store_inputs(i, h) | |
|
267 | tail = hm2.get_tail(n=3) | |
|
268 | assert list(map(get_source, tail)) == hist2 | |
|
269 | tail = ha.get_tail(n=3) | |
|
270 | assert list(map(get_source, tail)) == hist2 | |
|
271 | sid2 = hm2_last_sid() | |
|
272 | assert sid2 is not None | |
|
273 | assert ha_last_sid() == sid2 | |
|
274 | assert sid2 != sid1 | |
|
275 | ||
|
276 | # but hm1 still maintains its point of reference | |
|
277 | # and adding more entries to it doesn't change others | |
|
278 | # immediate perspective | |
|
279 | assert hm1_last_sid() == sid1 | |
|
280 | tail = hm1.get_tail(n=3) | |
|
281 | assert list(map(get_source, tail)) == hist1 | |
|
282 | ||
|
283 | hist3 = ["a=3", "b=3", "c=3"] | |
|
284 | for i, h in enumerate(hist3 + [""], start=5): | |
|
285 | hm1.store_inputs(i, h) | |
|
286 | tail = hm1.get_tail(n=7) | |
|
287 | assert list(map(get_source, tail)) == hist1 + [""] + hist3 | |
|
288 | tail = hm2.get_tail(n=3) | |
|
289 | assert list(map(get_source, tail)) == hist2 | |
|
290 | tail = ha.get_tail(n=3) | |
|
291 | assert list(map(get_source, tail)) == hist2 | |
|
292 | assert hm1_last_sid() == sid1 | |
|
293 | assert hm2_last_sid() == sid2 | |
|
294 | assert ha_last_sid() == sid2 |
General Comments 0
You need to be logged in to leave comments.
Login now