##// END OF EJS Templates
Merge pull request #4542 from ivanov/history-clear...
Min RK -
r13694:24e10561 merge
parent child Browse files
Show More
@@ -0,0 +1,1 b''
1 * bash completion updated with support for all ipython subcommands and flags, including nbconvert
@@ -0,0 +1,4 b''
1 * ``ipython history trim``: added ``--keep=<N>`` as an alias for the more verbose
2 ``--HistoryTrim.keep=<N>``
3 * new ``ipython history clear`` subcommand, which is the same as the newly supported
4 ``ipython history trim --keep=0``
@@ -1,119 +1,159 b''
1 1 # encoding: utf-8
2 2 """
3 3 An application for managing IPython history.
4 4
5 5 To be invoked as the `ipython history` subcommand.
6 6 """
7 7 from __future__ import print_function
8 8
9 9 import os
10 10 import sqlite3
11 11
12 12 from IPython.config.application import Application
13 13 from IPython.core.application import BaseIPythonApplication
14 14 from IPython.utils.traitlets import Bool, Int, Dict
15 from IPython.utils.io import ask_yes_no
15 16
16 17 trim_hist_help = """Trim the IPython history database to the last 1000 entries.
17 18
18 19 This actually copies the last 1000 entries to a new database, and then replaces
19 the old file with the new.
20 the old file with the new. Use the `--keep=` argument to specify a number
21 other than 1000.
20 22 """
21 23
24 clear_hist_help = """Clear the IPython history database, deleting all entries.
25
26 Because this is a destructive operation, IPython will prompt the user if they
27 really want to do this. Passing a `-f` flag will force clearing without a
28 prompt.
29
30 This is an handy alias to `ipython history trim --keep=0`
31 """
32
33
22 34 class HistoryTrim(BaseIPythonApplication):
23 35 description = trim_hist_help
24 36
25 37 backup = Bool(False, config=True,
26 38 help="Keep the old history file as history.sqlite.<N>")
27 39
28 40 keep = Int(1000, config=True,
29 41 help="Number of recent lines to keep in the database.")
30 42
31 43 flags = Dict(dict(
32 44 backup = ({'HistoryTrim' : {'backup' : True}},
33 "Set Application.log_level to 0, maximizing log output."
45 backup.get_metadata('help')
34 46 )
35 47 ))
48
49 aliases=Dict(dict(
50 keep = 'HistoryTrim.keep'
51 ))
36 52
37 53 def start(self):
38 54 profile_dir = self.profile_dir.location
39 55 hist_file = os.path.join(profile_dir, 'history.sqlite')
40 56 con = sqlite3.connect(hist_file)
41 57
42 58 # Grab the recent history from the current database.
43 59 inputs = list(con.execute('SELECT session, line, source, source_raw FROM '
44 60 'history ORDER BY session DESC, line DESC LIMIT ?', (self.keep+1,)))
45 61 if len(inputs) <= self.keep:
46 62 print("There are already at most %d entries in the history database." % self.keep)
47 print("Not doing anything.")
63 print("Not doing anything. Use --keep= argument to keep fewer entries")
48 64 return
49 65
50 66 print("Trimming history to the most recent %d entries." % self.keep)
51 67
52 68 inputs.pop() # Remove the extra element we got to check the length.
53 69 inputs.reverse()
54 first_session = inputs[0][0]
55 outputs = list(con.execute('SELECT session, line, output FROM '
56 'output_history WHERE session >= ?', (first_session,)))
57 sessions = list(con.execute('SELECT session, start, end, num_cmds, remark FROM '
58 'sessions WHERE session >= ?', (first_session,)))
70 if inputs:
71 first_session = inputs[0][0]
72 outputs = list(con.execute('SELECT session, line, output FROM '
73 'output_history WHERE session >= ?', (first_session,)))
74 sessions = list(con.execute('SELECT session, start, end, num_cmds, remark FROM '
75 'sessions WHERE session >= ?', (first_session,)))
59 76 con.close()
60 77
61 78 # Create the new history database.
62 79 new_hist_file = os.path.join(profile_dir, 'history.sqlite.new')
63 80 i = 0
64 81 while os.path.exists(new_hist_file):
65 82 # Make sure we don't interfere with an existing file.
66 83 i += 1
67 84 new_hist_file = os.path.join(profile_dir, 'history.sqlite.new'+str(i))
68 85 new_db = sqlite3.connect(new_hist_file)
69 86 new_db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
70 87 primary key autoincrement, start timestamp,
71 88 end timestamp, num_cmds integer, remark text)""")
72 89 new_db.execute("""CREATE TABLE IF NOT EXISTS history
73 90 (session integer, line integer, source text, source_raw text,
74 91 PRIMARY KEY (session, line))""")
75 92 new_db.execute("""CREATE TABLE IF NOT EXISTS output_history
76 93 (session integer, line integer, output text,
77 94 PRIMARY KEY (session, line))""")
78 95 new_db.commit()
79 96
80 97
81 with new_db:
82 # Add the recent history into the new database.
83 new_db.executemany('insert into sessions values (?,?,?,?,?)', sessions)
84 new_db.executemany('insert into history values (?,?,?,?)', inputs)
85 new_db.executemany('insert into output_history values (?,?,?)', outputs)
98 if inputs:
99 with new_db:
100 # Add the recent history into the new database.
101 new_db.executemany('insert into sessions values (?,?,?,?,?)', sessions)
102 new_db.executemany('insert into history values (?,?,?,?)', inputs)
103 new_db.executemany('insert into output_history values (?,?,?)', outputs)
86 104 new_db.close()
87 105
88 106 if self.backup:
89 107 i = 1
90 108 backup_hist_file = os.path.join(profile_dir, 'history.sqlite.old.%d' % i)
91 109 while os.path.exists(backup_hist_file):
92 110 i += 1
93 111 backup_hist_file = os.path.join(profile_dir, 'history.sqlite.old.%d' % i)
94 112 os.rename(hist_file, backup_hist_file)
95 113 print("Backed up longer history file to", backup_hist_file)
96 114 else:
97 115 os.remove(hist_file)
98 116
99 117 os.rename(new_hist_file, hist_file)
100 118
119 class HistoryClear(HistoryTrim):
120 description = clear_hist_help
121 keep = Int(0, config=False,
122 help="Number of recent lines to keep in the database.")
123
124 force = Bool(False, config=True,
125 help="Don't prompt user for confirmation")
126
127 flags = Dict(dict(
128 force = ({'HistoryClear' : {'force' : True}},
129 force.get_metadata('help')),
130 f = ({'HistoryTrim' : {'force' : True}},
131 force.get_metadata('help')
132 )
133 ))
134 aliases = Dict()
135
136 def start(self):
137 if self.force or ask_yes_no("Really delete all ipython history? ",
138 default="no", interrupt="no"):
139 HistoryTrim.start(self)
101 140
102 141 class HistoryApp(Application):
103 142 name = u'ipython-history'
104 143 description = "Manage the IPython history database."
105 144
106 145 subcommands = Dict(dict(
107 146 trim = (HistoryTrim, HistoryTrim.description.splitlines()[0]),
147 clear = (HistoryClear, HistoryClear.description.splitlines()[0]),
108 148 ))
109 149
110 150 def start(self):
111 151 if self.subapp is None:
112 152 print("No subcommand specified. Must specify one of: %s" % \
113 153 (self.subcommands.keys()))
114 154 print()
115 155 self.print_description()
116 156 self.print_subcommands()
117 157 self.exit(1)
118 158 else:
119 159 return self.subapp.start()
@@ -1,229 +1,232 b''
1 1 # encoding: utf-8
2 2 """
3 3 IO related utilities.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12 from __future__ import print_function
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 import os
18 18 import sys
19 19 import tempfile
20 20 from .capture import CapturedIO, capture_output
21 21 from .py3compat import string_types, input
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27
28 28 class IOStream:
29 29
30 30 def __init__(self,stream, fallback=None):
31 31 if not hasattr(stream,'write') or not hasattr(stream,'flush'):
32 32 if fallback is not None:
33 33 stream = fallback
34 34 else:
35 35 raise ValueError("fallback required, but not specified")
36 36 self.stream = stream
37 37 self._swrite = stream.write
38 38
39 39 # clone all methods not overridden:
40 40 def clone(meth):
41 41 return not hasattr(self, meth) and not meth.startswith('_')
42 42 for meth in filter(clone, dir(stream)):
43 43 setattr(self, meth, getattr(stream, meth))
44 44
45 45 def write(self,data):
46 46 try:
47 47 self._swrite(data)
48 48 except:
49 49 try:
50 50 # print handles some unicode issues which may trip a plain
51 51 # write() call. Emulate write() by using an empty end
52 52 # argument.
53 53 print(data, end='', file=self.stream)
54 54 except:
55 55 # if we get here, something is seriously broken.
56 56 print('ERROR - failed to write data to stream:', self.stream,
57 57 file=sys.stderr)
58 58
59 59 def writelines(self, lines):
60 60 if isinstance(lines, string_types):
61 61 lines = [lines]
62 62 for line in lines:
63 63 self.write(line)
64 64
65 65 # This class used to have a writeln method, but regular files and streams
66 66 # in Python don't have this method. We need to keep this completely
67 67 # compatible so we removed it.
68 68
69 69 @property
70 70 def closed(self):
71 71 return self.stream.closed
72 72
73 73 def close(self):
74 74 pass
75 75
76 76 # setup stdin/stdout/stderr to sys.stdin/sys.stdout/sys.stderr
77 77 devnull = open(os.devnull, 'a')
78 78 stdin = IOStream(sys.stdin, fallback=devnull)
79 79 stdout = IOStream(sys.stdout, fallback=devnull)
80 80 stderr = IOStream(sys.stderr, fallback=devnull)
81 81
82 82 class IOTerm:
83 83 """ Term holds the file or file-like objects for handling I/O operations.
84 84
85 85 These are normally just sys.stdin, sys.stdout and sys.stderr but for
86 86 Windows they can can replaced to allow editing the strings before they are
87 87 displayed."""
88 88
89 89 # In the future, having IPython channel all its I/O operations through
90 90 # this class will make it easier to embed it into other environments which
91 91 # are not a normal terminal (such as a GUI-based shell)
92 92 def __init__(self, stdin=None, stdout=None, stderr=None):
93 93 mymodule = sys.modules[__name__]
94 94 self.stdin = IOStream(stdin, mymodule.stdin)
95 95 self.stdout = IOStream(stdout, mymodule.stdout)
96 96 self.stderr = IOStream(stderr, mymodule.stderr)
97 97
98 98
99 99 class Tee(object):
100 100 """A class to duplicate an output stream to stdout/err.
101 101
102 102 This works in a manner very similar to the Unix 'tee' command.
103 103
104 104 When the object is closed or deleted, it closes the original file given to
105 105 it for duplication.
106 106 """
107 107 # Inspired by:
108 108 # http://mail.python.org/pipermail/python-list/2007-May/442737.html
109 109
110 110 def __init__(self, file_or_name, mode="w", channel='stdout'):
111 111 """Construct a new Tee object.
112 112
113 113 Parameters
114 114 ----------
115 115 file_or_name : filename or open filehandle (writable)
116 116 File that will be duplicated
117 117
118 118 mode : optional, valid mode for open().
119 119 If a filename was give, open with this mode.
120 120
121 121 channel : str, one of ['stdout', 'stderr']
122 122 """
123 123 if channel not in ['stdout', 'stderr']:
124 124 raise ValueError('Invalid channel spec %s' % channel)
125 125
126 126 if hasattr(file_or_name, 'write') and hasattr(file_or_name, 'seek'):
127 127 self.file = file_or_name
128 128 else:
129 129 self.file = open(file_or_name, mode)
130 130 self.channel = channel
131 131 self.ostream = getattr(sys, channel)
132 132 setattr(sys, channel, self)
133 133 self._closed = False
134 134
135 135 def close(self):
136 136 """Close the file and restore the channel."""
137 137 self.flush()
138 138 setattr(sys, self.channel, self.ostream)
139 139 self.file.close()
140 140 self._closed = True
141 141
142 142 def write(self, data):
143 143 """Write data to both channels."""
144 144 self.file.write(data)
145 145 self.ostream.write(data)
146 146 self.ostream.flush()
147 147
148 148 def flush(self):
149 149 """Flush both channels."""
150 150 self.file.flush()
151 151 self.ostream.flush()
152 152
153 153 def __del__(self):
154 154 if not self._closed:
155 155 self.close()
156 156
157 157
158 def ask_yes_no(prompt,default=None):
158 def ask_yes_no(prompt, default=None, interrupt=None):
159 159 """Asks a question and returns a boolean (y/n) answer.
160 160
161 161 If default is given (one of 'y','n'), it is used if the user input is
162 empty. Otherwise the question is repeated until an answer is given.
162 empty. If interrupt is given (one of 'y','n'), it is used if the user
163 presses Ctrl-C. Otherwise the question is repeated until an answer is
164 given.
163 165
164 166 An EOF is treated as the default answer. If there is no default, an
165 167 exception is raised to prevent infinite loops.
166 168
167 169 Valid answers are: y/yes/n/no (match is not case sensitive)."""
168 170
169 171 answers = {'y':True,'n':False,'yes':True,'no':False}
170 172 ans = None
171 173 while ans not in answers.keys():
172 174 try:
173 175 ans = input(prompt+' ').lower()
174 176 if not ans: # response was an empty string
175 177 ans = default
176 178 except KeyboardInterrupt:
177 pass
179 if interrupt:
180 ans = interrupt
178 181 except EOFError:
179 182 if default in answers.keys():
180 183 ans = default
181 184 print()
182 185 else:
183 186 raise
184 187
185 188 return answers[ans]
186 189
187 190
188 191 def temp_pyfile(src, ext='.py'):
189 192 """Make a temporary python file, return filename and filehandle.
190 193
191 194 Parameters
192 195 ----------
193 196 src : string or list of strings (no need for ending newlines if list)
194 197 Source code to be written to the file.
195 198
196 199 ext : optional, string
197 200 Extension for the generated file.
198 201
199 202 Returns
200 203 -------
201 204 (filename, open filehandle)
202 205 It is the caller's responsibility to close the open file and unlink it.
203 206 """
204 207 fname = tempfile.mkstemp(ext)[1]
205 208 f = open(fname,'w')
206 209 f.write(src)
207 210 f.flush()
208 211 return fname, f
209 212
210 213
211 214 def raw_print(*args, **kw):
212 215 """Raw print to sys.__stdout__, otherwise identical interface to print()."""
213 216
214 217 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
215 218 file=sys.__stdout__)
216 219 sys.__stdout__.flush()
217 220
218 221
219 222 def raw_print_err(*args, **kw):
220 223 """Raw print to sys.__stderr__, otherwise identical interface to print()."""
221 224
222 225 print(*args, sep=kw.get('sep', ' '), end=kw.get('end', '\n'),
223 226 file=sys.__stderr__)
224 227 sys.__stderr__.flush()
225 228
226 229
227 230 # Short aliases for quick debugging, do NOT use these in production code.
228 231 rprint = raw_print
229 232 rprinte = raw_print_err
@@ -1,115 +1,131 b''
1 1 # load with: . ipython-completion.bash
2 2
3 3 if [[ -n ${ZSH_VERSION-} ]]; then
4 4 autoload -Uz bashcompinit && bashcompinit
5 5 fi
6 6
7 7 _ipython_get_flags()
8 8 {
9 9 local url=$1
10 10 local var=$2
11 11 local dash=$3
12 12 if [[ "$url $var" == $__ipython_complete_last ]]; then
13 13 opts=$__ipython_complete_last_res
14 14 return
15 15 fi
16 16 # pylab and profile don't need the = and the
17 17 # version without simplifies the special cased completion
18 18 opts=$(ipython ${url} --help-all | grep -E "^-{1,2}[^-]" | sed -e "s/<.*//" -e "s/[^=]$/& /" -e "s/^--pylab=$//" -e "s/^--profile=$/--profile /")
19 19 __ipython_complete_last="$url $var"
20 20 __ipython_complete_last_res="$opts"
21 21 }
22 22
23 23 _ipython()
24 24 {
25 25 local cur=${COMP_WORDS[COMP_CWORD]}
26 26 local prev=${COMP_WORDS[COMP_CWORD - 1]}
27 local subcommands="notebook qtconsole console kernel profile locate history nbconvert"
27 local subcommands="notebook qtconsole console kernel profile locate history nbconvert "
28 28 local opts=""
29 29 if [ -z "$__ipython_complete_baseopts" ]; then
30 30 _ipython_get_flags baseopts
31 31 __ipython_complete_baseopts="${opts}"
32 32 fi
33 33 local baseopts="$__ipython_complete_baseopts"
34 34 local mode=""
35 35 for i in "${COMP_WORDS[@]}"; do
36 36 [ "$cur" = "$i" ] && break
37 37 if [[ ${subcommands} == *${i}* ]]; then
38 38 mode="$i"
39 39 break
40 40 elif [[ ${i} == "--"* ]]; then
41 41 mode="nosubcommand"
42 42 break
43 43 fi
44 44 done
45 45
46 46
47 47 if [[ ${cur} == -* ]]; then
48 48 case $mode in
49 "notebook" | "qtconsole" | "console" | "kernel")
49 "notebook" | "qtconsole" | "console" | "kernel" | "nbconvert")
50 50 _ipython_get_flags $mode
51 51 opts=$"${opts} ${baseopts}"
52 52 ;;
53 "locate" | "history" | "profile")
53 "locate" | "profile")
54 54 _ipython_get_flags $mode
55 ;;
56 "history")
57 if [[ $COMP_CWORD -ge 3 ]]; then
58 # 'history trim' and 'history clear' covered by next line
59 _ipython_get_flags history\ "${COMP_WORDS[2]}"
60 else
61 _ipython_get_flags $mode
62
63 fi
55 64 opts=$"${opts}"
56 65 ;;
57 66 *)
58 67 opts=$baseopts
59 68 esac
60 69 # don't drop the trailing space
61 70 local IFS=$'\t\n'
62 71 COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
63 72 return 0
64 73 elif [[ $mode == "profile" ]]; then
65 opts="list create locate"
74 opts="list create locate "
75 local IFS=$'\t\n'
66 76 COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
67 77 elif [[ $mode == "history" ]]; then
68 opts="trim"
78 if [[ $COMP_CWORD -ge 3 ]]; then
79 # drop into flags
80 opts="--"
81 else
82 opts="trim clear "
83 fi
84 local IFS=$'\t\n'
69 85 COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
70 86 elif [[ $mode == "locate" ]]; then
71 87 opts="profile"
72 88 COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
73 89 elif [[ ${prev} == "--pylab"* ]] || [[ ${prev} == "--gui"* ]]; then
74 90 if [ -z "$__ipython_complete_pylab" ]; then
75 91 __ipython_complete_pylab=`cat <<EOF | python -
76 92 try:
77 93 import IPython.core.shellapp as mod;
78 94 for k in mod.InteractiveShellApp.pylab.values:
79 95 print "%s " % k
80 96 except:
81 97 pass
82 98 EOF
83 99 `
84 100 fi
85 101 local IFS=$'\t\n'
86 102 COMPREPLY=( $(compgen -W "${__ipython_complete_pylab}" -- ${cur}) )
87 103 elif [[ ${prev} == "--profile"* ]]; then
88 104 if [ -z "$__ipython_complete_profiles" ]; then
89 105 __ipython_complete_profiles=`cat <<EOF | python -
90 106 try:
91 107 import IPython.core.profileapp
92 108 for k in IPython.core.profileapp.list_bundled_profiles():
93 109 print "%s " % k
94 110 p = IPython.core.profileapp.ProfileList()
95 111 for k in IPython.core.profileapp.list_profiles_in(p.ipython_dir):
96 112 print "%s " % k
97 113 except:
98 114 pass
99 115 EOF
100 116 `
101 117 fi
102 118 local IFS=$'\t\n'
103 119 COMPREPLY=( $(compgen -W "${__ipython_complete_profiles}" -- ${cur}) )
104 120 else
105 121 if [ "$COMP_CWORD" == 1 ]; then
106 122 local IFS=$'\t\n'
107 123 local sub=$(echo $subcommands | sed -e "s/ / \t/g")
108 124 COMPREPLY=( $(compgen -W "${sub}" -- ${cur}) )
109 125 else
110 126 COMPREPLY=( $(compgen -f -- ${cur}) )
111 127 fi
112 128 fi
113 129
114 130 }
115 131 complete -o default -o nospace -F _ipython ipython
General Comments 0
You need to be logged in to leave comments. Login now