|
@@
-6,7
+6,7
|
|
6
|
|
|
6
|
|
|
7
|
import atexit
|
|
7
|
import atexit
|
|
8
|
import datetime
|
|
8
|
import datetime
|
|
9
|
import os
|
|
9
|
from pathlib import Path
|
|
10
|
import re
|
|
10
|
import re
|
|
11
|
import sqlite3
|
|
11
|
import sqlite3
|
|
12
|
import threading
|
|
12
|
import threading
|
|
@@
-16,8
+16,8
from decorator import decorator
|
|
16
|
from IPython.utils.decorators import undoc
|
|
16
|
from IPython.utils.decorators import undoc
|
|
17
|
from IPython.paths import locate_profile
|
|
17
|
from IPython.paths import locate_profile
|
|
18
|
from traitlets import (
|
|
18
|
from traitlets import (
|
|
19
|
Any, Bool, Dict, Instance, Integer, List, Unicode, TraitError,
|
|
19
|
Any, Bool, Dict, Instance, Integer, List, Unicode, Union, TraitError,
|
|
20
|
default, observe,
|
|
20
|
default, observe
|
|
21
|
)
|
|
21
|
)
|
|
22
|
|
|
22
|
|
|
23
|
#-----------------------------------------------------------------------------
|
|
23
|
#-----------------------------------------------------------------------------
|
|
@@
-27,17
+27,17
from traitlets import (
|
|
27
|
@undoc
|
|
27
|
@undoc
|
|
28
|
class DummyDB(object):
|
|
28
|
class DummyDB(object):
|
|
29
|
"""Dummy DB that will act as a black hole for history.
|
|
29
|
"""Dummy DB that will act as a black hole for history.
|
|
30
|
|
|
30
|
|
|
31
|
Only used in the absence of sqlite"""
|
|
31
|
Only used in the absence of sqlite"""
|
|
32
|
def execute(*args, **kwargs):
|
|
32
|
def execute(*args, **kwargs):
|
|
33
|
return []
|
|
33
|
return []
|
|
34
|
|
|
34
|
|
|
35
|
def commit(self, *args, **kwargs):
|
|
35
|
def commit(self, *args, **kwargs):
|
|
36
|
pass
|
|
36
|
pass
|
|
37
|
|
|
37
|
|
|
38
|
def __enter__(self, *args, **kwargs):
|
|
38
|
def __enter__(self, *args, **kwargs):
|
|
39
|
pass
|
|
39
|
pass
|
|
40
|
|
|
40
|
|
|
41
|
def __exit__(self, *args, **kwargs):
|
|
41
|
def __exit__(self, *args, **kwargs):
|
|
42
|
pass
|
|
42
|
pass
|
|
43
|
|
|
43
|
|
|
@@
-73,17
+73,18
def catch_corrupt_db(f, self, *a, **kw):
|
|
73
|
if self._corrupt_db_counter > self._corrupt_db_limit:
|
|
73
|
if self._corrupt_db_counter > self._corrupt_db_limit:
|
|
74
|
self.hist_file = ':memory:'
|
|
74
|
self.hist_file = ':memory:'
|
|
75
|
self.log.error("Failed to load history too many times, history will not be saved.")
|
|
75
|
self.log.error("Failed to load history too many times, history will not be saved.")
|
|
76
|
elif os.path.isfile(self.hist_file):
|
|
76
|
elif self.hist_file.is_file():
|
|
77
|
# move the file out of the way
|
|
77
|
# move the file out of the way
|
|
78
|
base, ext = os.path.splitext(self.hist_file)
|
|
78
|
base = str(self.hist_file.parent / self.hist_file.stem)
|
|
79
|
size = os.stat(self.hist_file).st_size
|
|
79
|
ext = self.hist_file.suffix
|
|
|
|
|
80
|
size = self.hist_file.stat().st_size
|
|
80
|
if size >= _SAVE_DB_SIZE:
|
|
81
|
if size >= _SAVE_DB_SIZE:
|
|
81
|
# if there's significant content, avoid clobbering
|
|
82
|
# if there's significant content, avoid clobbering
|
|
82
|
now = datetime.datetime.now().isoformat().replace(':', '.')
|
|
83
|
now = datetime.datetime.now().isoformat().replace(':', '.')
|
|
83
|
newpath = base + '-corrupt-' + now + ext
|
|
84
|
newpath = base + '-corrupt-' + now + ext
|
|
84
|
# don't clobber previous corrupt backups
|
|
85
|
# don't clobber previous corrupt backups
|
|
85
|
for i in range(100):
|
|
86
|
for i in range(100):
|
|
86
|
if not os.path.isfile(newpath):
|
|
87
|
if not Path(newpath).exists():
|
|
87
|
break
|
|
88
|
break
|
|
88
|
else:
|
|
89
|
else:
|
|
89
|
newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
|
|
90
|
newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
|
|
@@
-91,14
+92,14
def catch_corrupt_db(f, self, *a, **kw):
|
|
91
|
# not much content, possibly empty; don't worry about clobbering
|
|
92
|
# not much content, possibly empty; don't worry about clobbering
|
|
92
|
# maybe we should just delete it?
|
|
93
|
# maybe we should just delete it?
|
|
93
|
newpath = base + '-corrupt' + ext
|
|
94
|
newpath = base + '-corrupt' + ext
|
|
94
|
os.rename(self.hist_file, newpath)
|
|
95
|
self.hist_file.rename(newpath)
|
|
95
|
self.log.error("History file was moved to %s and a new file created.", newpath)
|
|
96
|
self.log.error("History file was moved to %s and a new file created.", newpath)
|
|
96
|
self.init_db()
|
|
97
|
self.init_db()
|
|
97
|
return []
|
|
98
|
return []
|
|
98
|
else:
|
|
99
|
else:
|
|
99
|
# Failed with :memory:, something serious is wrong
|
|
100
|
# Failed with :memory:, something serious is wrong
|
|
100
|
raise
|
|
101
|
raise
|
|
101
|
|
|
102
|
|
|
102
|
class HistoryAccessorBase(LoggingConfigurable):
|
|
103
|
class HistoryAccessorBase(LoggingConfigurable):
|
|
103
|
"""An abstract class for History Accessors """
|
|
104
|
"""An abstract class for History Accessors """
|
|
104
|
|
|
105
|
|
|
@@
-118,7
+119,7
class HistoryAccessorBase(LoggingConfigurable):
|
|
118
|
|
|
119
|
|
|
119
|
class HistoryAccessor(HistoryAccessorBase):
|
|
120
|
class HistoryAccessor(HistoryAccessorBase):
|
|
120
|
"""Access the history database without adding to it.
|
|
121
|
"""Access the history database without adding to it.
|
|
121
|
|
|
122
|
|
|
122
|
This is intended for use by standalone history tools. IPython shells use
|
|
123
|
This is intended for use by standalone history tools. IPython shells use
|
|
123
|
HistoryManager, below, which is a subclass of this."""
|
|
124
|
HistoryManager, below, which is a subclass of this."""
|
|
124
|
|
|
125
|
|
|
@@
-128,37
+129,38
class HistoryAccessor(HistoryAccessorBase):
|
|
128
|
_corrupt_db_limit = 2
|
|
129
|
_corrupt_db_limit = 2
|
|
129
|
|
|
130
|
|
|
130
|
# String holding the path to the history file
|
|
131
|
# String holding the path to the history file
|
|
131
|
hist_file = Unicode(
|
|
132
|
hist_file = Union([Instance(Path), Unicode()],
|
|
132
|
help="""Path to file to use for SQLite history database.
|
|
133
|
help="""Path to file to use for SQLite history database.
|
|
133
|
|
|
134
|
|
|
134
|
By default, IPython will put the history database in the IPython
|
|
135
|
By default, IPython will put the history database in the IPython
|
|
135
|
profile directory. If you would rather share one history among
|
|
136
|
profile directory. If you would rather share one history among
|
|
136
|
profiles, you can set this value in each, so that they are consistent.
|
|
137
|
profiles, you can set this value in each, so that they are consistent.
|
|
137
|
|
|
138
|
|
|
138
|
Due to an issue with fcntl, SQLite is known to misbehave on some NFS
|
|
139
|
Due to an issue with fcntl, SQLite is known to misbehave on some NFS
|
|
139
|
mounts. If you see IPython hanging, try setting this to something on a
|
|
140
|
mounts. If you see IPython hanging, try setting this to something on a
|
|
140
|
local disk, e.g::
|
|
141
|
local disk, e.g::
|
|
141
|
|
|
142
|
|
|
142
|
ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
|
|
143
|
ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
|
|
143
|
|
|
144
|
|
|
144
|
you can also use the specific value `:memory:` (including the colon
|
|
145
|
you can also use the specific value `:memory:` (including the colon
|
|
145
|
at both end but not the back ticks), to avoid creating an history file.
|
|
146
|
at both end but not the back ticks), to avoid creating an history file.
|
|
146
|
|
|
147
|
|
|
147
|
""").tag(config=True)
|
|
148
|
"""
|
|
148
|
|
|
149
|
).tag(config=True)
|
|
|
|
|
150
|
|
|
149
|
enabled = Bool(True,
|
|
151
|
enabled = Bool(True,
|
|
150
|
help="""enable the SQLite history
|
|
152
|
help="""enable the SQLite history
|
|
151
|
|
|
153
|
|
|
152
|
set enabled=False to disable the SQLite history,
|
|
154
|
set enabled=False to disable the SQLite history,
|
|
153
|
in which case there will be no stored history, no SQLite connection,
|
|
155
|
in which case there will be no stored history, no SQLite connection,
|
|
154
|
and no background saving thread. This may be necessary in some
|
|
156
|
and no background saving thread. This may be necessary in some
|
|
155
|
threaded environments where IPython is embedded.
|
|
157
|
threaded environments where IPython is embedded.
|
|
156
|
"""
|
|
158
|
"""
|
|
157
|
).tag(config=True)
|
|
159
|
).tag(config=True)
|
|
158
|
|
|
160
|
|
|
159
|
connection_options = Dict(
|
|
161
|
connection_options = Dict(
|
|
160
|
help="""Options for configuring the SQLite connection
|
|
162
|
help="""Options for configuring the SQLite connection
|
|
161
|
|
|
163
|
|
|
162
|
These options are passed as keyword args to sqlite3.connect
|
|
164
|
These options are passed as keyword args to sqlite3.connect
|
|
163
|
when establishing database connections.
|
|
165
|
when establishing database connections.
|
|
164
|
"""
|
|
166
|
"""
|
|
@@
-175,10
+177,10
class HistoryAccessor(HistoryAccessorBase):
|
|
175
|
msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
|
|
177
|
msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
|
|
176
|
(self.__class__.__name__, new)
|
|
178
|
(self.__class__.__name__, new)
|
|
177
|
raise TraitError(msg)
|
|
179
|
raise TraitError(msg)
|
|
178
|
|
|
180
|
|
|
179
|
def __init__(self, profile='default', hist_file=u'', **traits):
|
|
181
|
def __init__(self, profile='default', hist_file="", **traits):
|
|
180
|
"""Create a new history accessor.
|
|
182
|
"""Create a new history accessor.
|
|
181
|
|
|
183
|
|
|
182
|
Parameters
|
|
184
|
Parameters
|
|
183
|
----------
|
|
185
|
----------
|
|
184
|
profile : str
|
|
186
|
profile : str
|
|
@@
-196,33
+198,35
class HistoryAccessor(HistoryAccessorBase):
|
|
196
|
# set by config
|
|
198
|
# set by config
|
|
197
|
if hist_file:
|
|
199
|
if hist_file:
|
|
198
|
self.hist_file = hist_file
|
|
200
|
self.hist_file = hist_file
|
|
199
|
|
|
201
|
|
|
200
|
if self.hist_file == u'':
|
|
202
|
try:
|
|
|
|
|
203
|
self.hist_file
|
|
|
|
|
204
|
except TraitError:
|
|
201
|
# No one has set the hist_file, yet.
|
|
205
|
# No one has set the hist_file, yet.
|
|
202
|
self.hist_file = self._get_hist_file_name(profile)
|
|
206
|
self.hist_file = self._get_hist_file_name(profile)
|
|
203
|
|
|
207
|
|
|
204
|
self.init_db()
|
|
208
|
self.init_db()
|
|
205
|
|
|
209
|
|
|
206
|
def _get_hist_file_name(self, profile='default'):
|
|
210
|
def _get_hist_file_name(self, profile='default'):
|
|
207
|
"""Find the history file for the given profile name.
|
|
211
|
"""Find the history file for the given profile name.
|
|
208
|
|
|
212
|
|
|
209
|
This is overridden by the HistoryManager subclass, to use the shell's
|
|
213
|
This is overridden by the HistoryManager subclass, to use the shell's
|
|
210
|
active profile.
|
|
214
|
active profile.
|
|
211
|
|
|
215
|
|
|
212
|
Parameters
|
|
216
|
Parameters
|
|
213
|
----------
|
|
217
|
----------
|
|
214
|
profile : str
|
|
218
|
profile : str
|
|
215
|
The name of a profile which has a history file.
|
|
219
|
The name of a profile which has a history file.
|
|
216
|
"""
|
|
220
|
"""
|
|
217
|
return os.path.join(locate_profile(profile), 'history.sqlite')
|
|
221
|
return Path(locate_profile(profile)) / 'history.sqlite'
|
|
218
|
|
|
222
|
|
|
219
|
@catch_corrupt_db
|
|
223
|
@catch_corrupt_db
|
|
220
|
def init_db(self):
|
|
224
|
def init_db(self):
|
|
221
|
"""Connect to the database, and create tables if necessary."""
|
|
225
|
"""Connect to the database, and create tables if necessary."""
|
|
222
|
if not self.enabled:
|
|
226
|
if not self.enabled:
|
|
223
|
self.db = DummyDB()
|
|
227
|
self.db = DummyDB()
|
|
224
|
return
|
|
228
|
return
|
|
225
|
|
|
229
|
|
|
226
|
# use detect_types so that timestamps return datetime objects
|
|
230
|
# use detect_types so that timestamps return datetime objects
|
|
227
|
kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
|
|
231
|
kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
|
|
228
|
kwargs.update(self.connection_options)
|
|
232
|
kwargs.update(self.connection_options)
|
|
@@
-290,7
+294,7
class HistoryAccessor(HistoryAccessorBase):
|
|
290
|
|
|
294
|
|
|
291
|
Returns
|
|
295
|
Returns
|
|
292
|
-------
|
|
296
|
-------
|
|
293
|
|
|
297
|
|
|
294
|
session_id : int
|
|
298
|
session_id : int
|
|
295
|
Session ID number
|
|
299
|
Session ID number
|
|
296
|
start : datetime
|
|
300
|
start : datetime
|
|
@@
-308,7
+312,7
class HistoryAccessor(HistoryAccessorBase):
|
|
308
|
@catch_corrupt_db
|
|
312
|
@catch_corrupt_db
|
|
309
|
def get_last_session_id(self):
|
|
313
|
def get_last_session_id(self):
|
|
310
|
"""Get the last session ID currently in the database.
|
|
314
|
"""Get the last session ID currently in the database.
|
|
311
|
|
|
315
|
|
|
312
|
Within IPython, this should be the same as the value stored in
|
|
316
|
Within IPython, this should be the same as the value stored in
|
|
313
|
:attr:`HistoryManager.session_number`.
|
|
317
|
:attr:`HistoryManager.session_number`.
|
|
314
|
"""
|
|
318
|
"""
|
|
@@
-384,7
+388,7
class HistoryAccessor(HistoryAccessorBase):
|
|
384
|
if n is not None:
|
|
388
|
if n is not None:
|
|
385
|
return reversed(list(cur))
|
|
389
|
return reversed(list(cur))
|
|
386
|
return cur
|
|
390
|
return cur
|
|
387
|
|
|
391
|
|
|
388
|
@catch_corrupt_db
|
|
392
|
@catch_corrupt_db
|
|
389
|
def get_range(self, session, start=1, stop=None, raw=True,output=False):
|
|
393
|
def get_range(self, session, start=1, stop=None, raw=True,output=False):
|
|
390
|
"""Retrieve input by session.
|
|
394
|
"""Retrieve input by session.
|
|
@@
-461,7
+465,7
class HistoryManager(HistoryAccessor):
|
|
461
|
@default('dir_hist')
|
|
465
|
@default('dir_hist')
|
|
462
|
def _dir_hist_default(self):
|
|
466
|
def _dir_hist_default(self):
|
|
463
|
try:
|
|
467
|
try:
|
|
464
|
return [os.getcwd()]
|
|
468
|
return [Path.cwd()]
|
|
465
|
except OSError:
|
|
469
|
except OSError:
|
|
466
|
return []
|
|
470
|
return []
|
|
467
|
|
|
471
|
|
|
@@
-473,7
+477,7
class HistoryManager(HistoryAccessor):
|
|
473
|
|
|
477
|
|
|
474
|
# The number of the current session in the history database
|
|
478
|
# The number of the current session in the history database
|
|
475
|
session_number = Integer()
|
|
479
|
session_number = Integer()
|
|
476
|
|
|
480
|
|
|
477
|
db_log_output = Bool(False,
|
|
481
|
db_log_output = Bool(False,
|
|
478
|
help="Should the history database include output? (default: no)"
|
|
482
|
help="Should the history database include output? (default: no)"
|
|
479
|
).tag(config=True)
|
|
483
|
).tag(config=True)
|
|
@@
-484,12
+488,12
class HistoryManager(HistoryAccessor):
|
|
484
|
# The input and output caches
|
|
488
|
# The input and output caches
|
|
485
|
db_input_cache = List()
|
|
489
|
db_input_cache = List()
|
|
486
|
db_output_cache = List()
|
|
490
|
db_output_cache = List()
|
|
487
|
|
|
491
|
|
|
488
|
# History saving in separate thread
|
|
492
|
# History saving in separate thread
|
|
489
|
save_thread = Instance('IPython.core.history.HistorySavingThread',
|
|
493
|
save_thread = Instance('IPython.core.history.HistorySavingThread',
|
|
490
|
allow_none=True)
|
|
494
|
allow_none=True)
|
|
491
|
save_flag = Instance(threading.Event, allow_none=True)
|
|
495
|
save_flag = Instance(threading.Event, allow_none=True)
|
|
492
|
|
|
496
|
|
|
493
|
# Private interface
|
|
497
|
# Private interface
|
|
494
|
# Variables used to store the three last inputs from the user. On each new
|
|
498
|
# Variables used to store the three last inputs from the user. On each new
|
|
495
|
# history update, we populate the user's namespace with these, shifted as
|
|
499
|
# history update, we populate the user's namespace with these, shifted as
|
|
@@
-513,37
+517,37
class HistoryManager(HistoryAccessor):
|
|
513
|
self.save_flag = threading.Event()
|
|
517
|
self.save_flag = threading.Event()
|
|
514
|
self.db_input_cache_lock = threading.Lock()
|
|
518
|
self.db_input_cache_lock = threading.Lock()
|
|
515
|
self.db_output_cache_lock = threading.Lock()
|
|
519
|
self.db_output_cache_lock = threading.Lock()
|
|
516
|
|
|
520
|
|
|
517
|
try:
|
|
521
|
try:
|
|
518
|
self.new_session()
|
|
522
|
self.new_session()
|
|
519
|
except sqlite3.OperationalError:
|
|
523
|
except sqlite3.OperationalError:
|
|
520
|
self.log.error("Failed to create history session in %s. History will not be saved.",
|
|
524
|
self.log.error("Failed to create history session in %s. History will not be saved.",
|
|
521
|
self.hist_file, exc_info=True)
|
|
525
|
self.hist_file, exc_info=True)
|
|
522
|
self.hist_file = ':memory:'
|
|
526
|
self.hist_file = ':memory:'
|
|
523
|
|
|
527
|
|
|
524
|
if self.enabled and self.hist_file != ':memory:':
|
|
528
|
if self.enabled and self.hist_file != ':memory:':
|
|
525
|
self.save_thread = HistorySavingThread(self)
|
|
529
|
self.save_thread = HistorySavingThread(self)
|
|
526
|
self.save_thread.start()
|
|
530
|
self.save_thread.start()
|
|
527
|
|
|
531
|
|
|
528
|
def _get_hist_file_name(self, profile=None):
|
|
532
|
def _get_hist_file_name(self, profile=None):
|
|
529
|
"""Get default history file name based on the Shell's profile.
|
|
533
|
"""Get default history file name based on the Shell's profile.
|
|
530
|
|
|
534
|
|
|
531
|
The profile parameter is ignored, but must exist for compatibility with
|
|
535
|
The profile parameter is ignored, but must exist for compatibility with
|
|
532
|
the parent class."""
|
|
536
|
the parent class."""
|
|
533
|
profile_dir = self.shell.profile_dir.location
|
|
537
|
profile_dir = self.shell.profile_dir.location
|
|
534
|
return os.path.join(profile_dir, 'history.sqlite')
|
|
538
|
return Path(profile_dir)/'history.sqlite'
|
|
535
|
|
|
539
|
|
|
536
|
@only_when_enabled
|
|
540
|
@only_when_enabled
|
|
537
|
def new_session(self, conn=None):
|
|
541
|
def new_session(self, conn=None):
|
|
538
|
"""Get a new session number."""
|
|
542
|
"""Get a new session number."""
|
|
539
|
if conn is None:
|
|
543
|
if conn is None:
|
|
540
|
conn = self.db
|
|
544
|
conn = self.db
|
|
541
|
|
|
545
|
|
|
542
|
with conn:
|
|
546
|
with conn:
|
|
543
|
cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
|
|
547
|
cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
|
|
544
|
NULL, "") """, (datetime.datetime.now(),))
|
|
548
|
NULL, "") """, (datetime.datetime.now(),))
|
|
545
|
self.session_number = cur.lastrowid
|
|
549
|
self.session_number = cur.lastrowid
|
|
546
|
|
|
550
|
|
|
547
|
def end_session(self):
|
|
551
|
def end_session(self):
|
|
548
|
"""Close the database session, filling in the end time and line count."""
|
|
552
|
"""Close the database session, filling in the end time and line count."""
|
|
549
|
self.writeout_cache()
|
|
553
|
self.writeout_cache()
|
|
@@
-552,20
+556,20
class HistoryManager(HistoryAccessor):
|
|
552
|
session==?""", (datetime.datetime.now(),
|
|
556
|
session==?""", (datetime.datetime.now(),
|
|
553
|
len(self.input_hist_parsed)-1, self.session_number))
|
|
557
|
len(self.input_hist_parsed)-1, self.session_number))
|
|
554
|
self.session_number = 0
|
|
558
|
self.session_number = 0
|
|
555
|
|
|
559
|
|
|
556
|
def name_session(self, name):
|
|
560
|
def name_session(self, name):
|
|
557
|
"""Give the current session a name in the history database."""
|
|
561
|
"""Give the current session a name in the history database."""
|
|
558
|
with self.db:
|
|
562
|
with self.db:
|
|
559
|
self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
|
|
563
|
self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
|
|
560
|
(name, self.session_number))
|
|
564
|
(name, self.session_number))
|
|
561
|
|
|
565
|
|
|
562
|
def reset(self, new_session=True):
|
|
566
|
def reset(self, new_session=True):
|
|
563
|
"""Clear the session history, releasing all object references, and
|
|
567
|
"""Clear the session history, releasing all object references, and
|
|
564
|
optionally open a new session."""
|
|
568
|
optionally open a new session."""
|
|
565
|
self.output_hist.clear()
|
|
569
|
self.output_hist.clear()
|
|
566
|
# The directory history can't be completely empty
|
|
570
|
# The directory history can't be completely empty
|
|
567
|
self.dir_hist[:] = [os.getcwd()]
|
|
571
|
self.dir_hist[:] = [Path.cwd()]
|
|
568
|
|
|
572
|
|
|
569
|
if new_session:
|
|
573
|
if new_session:
|
|
570
|
if self.session_number:
|
|
574
|
if self.session_number:
|
|
571
|
self.end_session()
|
|
575
|
self.end_session()
|
|
@@
-588,7
+592,7
class HistoryManager(HistoryAccessor):
|
|
588
|
|
|
592
|
|
|
589
|
Returns
|
|
593
|
Returns
|
|
590
|
-------
|
|
594
|
-------
|
|
591
|
|
|
595
|
|
|
592
|
session_id : int
|
|
596
|
session_id : int
|
|
593
|
Session ID number
|
|
597
|
Session ID number
|
|
594
|
start : datetime
|
|
598
|
start : datetime
|
|
@@
-609,7
+613,7
class HistoryManager(HistoryAccessor):
|
|
609
|
"""Get input and output history from the current session. Called by
|
|
613
|
"""Get input and output history from the current session. Called by
|
|
610
|
get_range, and takes similar parameters."""
|
|
614
|
get_range, and takes similar parameters."""
|
|
611
|
input_hist = self.input_hist_raw if raw else self.input_hist_parsed
|
|
615
|
input_hist = self.input_hist_raw if raw else self.input_hist_parsed
|
|
612
|
|
|
616
|
|
|
613
|
n = len(input_hist)
|
|
617
|
n = len(input_hist)
|
|
614
|
if start < 0:
|
|
618
|
if start < 0:
|
|
615
|
start += n
|
|
619
|
start += n
|
|
@@
-617,17
+621,17
class HistoryManager(HistoryAccessor):
|
|
617
|
stop = n
|
|
621
|
stop = n
|
|
618
|
elif stop < 0:
|
|
622
|
elif stop < 0:
|
|
619
|
stop += n
|
|
623
|
stop += n
|
|
620
|
|
|
624
|
|
|
621
|
for i in range(start, stop):
|
|
625
|
for i in range(start, stop):
|
|
622
|
if output:
|
|
626
|
if output:
|
|
623
|
line = (input_hist[i], self.output_hist_reprs.get(i))
|
|
627
|
line = (input_hist[i], self.output_hist_reprs.get(i))
|
|
624
|
else:
|
|
628
|
else:
|
|
625
|
line = input_hist[i]
|
|
629
|
line = input_hist[i]
|
|
626
|
yield (0, i, line)
|
|
630
|
yield (0, i, line)
|
|
627
|
|
|
631
|
|
|
628
|
def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
|
|
632
|
def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
|
|
629
|
"""Retrieve input by session.
|
|
633
|
"""Retrieve input by session.
|
|
630
|
|
|
634
|
|
|
631
|
Parameters
|
|
635
|
Parameters
|
|
632
|
----------
|
|
636
|
----------
|
|
633
|
session : int
|
|
637
|
session : int
|
|
@@
-645,7
+649,7
class HistoryManager(HistoryAccessor):
|
|
645
|
objects for the current session, or text reprs from previous
|
|
649
|
objects for the current session, or text reprs from previous
|
|
646
|
sessions if db_log_output was enabled at the time. Where no output
|
|
650
|
sessions if db_log_output was enabled at the time. Where no output
|
|
647
|
is found, None is used.
|
|
651
|
is found, None is used.
|
|
648
|
|
|
652
|
|
|
649
|
Returns
|
|
653
|
Returns
|
|
650
|
-------
|
|
654
|
-------
|
|
651
|
entries
|
|
655
|
entries
|
|
@@
-709,7
+713,7
class HistoryManager(HistoryAccessor):
|
|
709
|
'_ii': self._ii,
|
|
713
|
'_ii': self._ii,
|
|
710
|
'_iii': self._iii,
|
|
714
|
'_iii': self._iii,
|
|
711
|
new_i : self._i00 }
|
|
715
|
new_i : self._i00 }
|
|
712
|
|
|
716
|
|
|
713
|
if self.shell is not None:
|
|
717
|
if self.shell is not None:
|
|
714
|
self.shell.push(to_main, interactive=False)
|
|
718
|
self.shell.push(to_main, interactive=False)
|
|
715
|
|
|
719
|
|