##// END OF EJS Templates
Allow new style aliases to be stored
Thomas Kluyver -
Show More
@@ -1,293 +1,301 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 System command aliases.
3 System command aliases.
4
4
5 Authors:
5 Authors:
6
6
7 * Fernando Perez
7 * Fernando Perez
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License.
14 # Distributed under the terms of the BSD License.
15 #
15 #
16 # The full license is in the file COPYING.txt, distributed with this software.
16 # The full license is in the file COPYING.txt, distributed with this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import __builtin__
23 import __builtin__
24 import keyword
24 import keyword
25 import os
25 import os
26 import re
26 import re
27 import sys
27 import sys
28
28
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.core.splitinput import split_user_input
30 from IPython.core.splitinput import split_user_input
31
31
32 from IPython.utils.traitlets import List, Instance
32 from IPython.utils.traitlets import List, Instance
33 from IPython.utils.warn import warn, error
33 from IPython.utils.warn import warn, error
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Utilities
36 # Utilities
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 # This is used as the pattern for calls to split_user_input.
39 # This is used as the pattern for calls to split_user_input.
40 shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)')
40 shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)')
41
41
42 def default_aliases():
42 def default_aliases():
43 """Return list of shell aliases to auto-define.
43 """Return list of shell aliases to auto-define.
44 """
44 """
45 # Note: the aliases defined here should be safe to use on a kernel
45 # Note: the aliases defined here should be safe to use on a kernel
46 # regardless of what frontend it is attached to. Frontends that use a
46 # regardless of what frontend it is attached to. Frontends that use a
47 # kernel in-process can define additional aliases that will only work in
47 # kernel in-process can define additional aliases that will only work in
48 # their case. For example, things like 'less' or 'clear' that manipulate
48 # their case. For example, things like 'less' or 'clear' that manipulate
49 # the terminal should NOT be declared here, as they will only work if the
49 # the terminal should NOT be declared here, as they will only work if the
50 # kernel is running inside a true terminal, and not over the network.
50 # kernel is running inside a true terminal, and not over the network.
51
51
52 if os.name == 'posix':
52 if os.name == 'posix':
53 default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
53 default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
54 ('mv', 'mv -i'), ('rm', 'rm -i'), ('cp', 'cp -i'),
54 ('mv', 'mv -i'), ('rm', 'rm -i'), ('cp', 'cp -i'),
55 ('cat', 'cat'),
55 ('cat', 'cat'),
56 ]
56 ]
57 # Useful set of ls aliases. The GNU and BSD options are a little
57 # Useful set of ls aliases. The GNU and BSD options are a little
58 # different, so we make aliases that provide as similar as possible
58 # different, so we make aliases that provide as similar as possible
59 # behavior in ipython, by passing the right flags for each platform
59 # behavior in ipython, by passing the right flags for each platform
60 if sys.platform.startswith('linux'):
60 if sys.platform.startswith('linux'):
61 ls_aliases = [('ls', 'ls -F --color'),
61 ls_aliases = [('ls', 'ls -F --color'),
62 # long ls
62 # long ls
63 ('ll', 'ls -F -o --color'),
63 ('ll', 'ls -F -o --color'),
64 # ls normal files only
64 # ls normal files only
65 ('lf', 'ls -F -o --color %l | grep ^-'),
65 ('lf', 'ls -F -o --color %l | grep ^-'),
66 # ls symbolic links
66 # ls symbolic links
67 ('lk', 'ls -F -o --color %l | grep ^l'),
67 ('lk', 'ls -F -o --color %l | grep ^l'),
68 # directories or links to directories,
68 # directories or links to directories,
69 ('ldir', 'ls -F -o --color %l | grep /$'),
69 ('ldir', 'ls -F -o --color %l | grep /$'),
70 # things which are executable
70 # things which are executable
71 ('lx', 'ls -F -o --color %l | grep ^-..x'),
71 ('lx', 'ls -F -o --color %l | grep ^-..x'),
72 ]
72 ]
73 else:
73 else:
74 # BSD, OSX, etc.
74 # BSD, OSX, etc.
75 ls_aliases = [('ls', 'ls -F -G'),
75 ls_aliases = [('ls', 'ls -F -G'),
76 # long ls
76 # long ls
77 ('ll', 'ls -F -l -G'),
77 ('ll', 'ls -F -l -G'),
78 # ls normal files only
78 # ls normal files only
79 ('lf', 'ls -F -l -G %l | grep ^-'),
79 ('lf', 'ls -F -l -G %l | grep ^-'),
80 # ls symbolic links
80 # ls symbolic links
81 ('lk', 'ls -F -l -G %l | grep ^l'),
81 ('lk', 'ls -F -l -G %l | grep ^l'),
82 # directories or links to directories,
82 # directories or links to directories,
83 ('ldir', 'ls -F -G -l %l | grep /$'),
83 ('ldir', 'ls -F -G -l %l | grep /$'),
84 # things which are executable
84 # things which are executable
85 ('lx', 'ls -F -l -G %l | grep ^-..x'),
85 ('lx', 'ls -F -l -G %l | grep ^-..x'),
86 ]
86 ]
87 default_aliases = default_aliases + ls_aliases
87 default_aliases = default_aliases + ls_aliases
88 elif os.name in ['nt', 'dos']:
88 elif os.name in ['nt', 'dos']:
89 default_aliases = [('ls', 'dir /on'),
89 default_aliases = [('ls', 'dir /on'),
90 ('ddir', 'dir /ad /on'), ('ldir', 'dir /ad /on'),
90 ('ddir', 'dir /ad /on'), ('ldir', 'dir /ad /on'),
91 ('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
91 ('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
92 ('echo', 'echo'), ('ren', 'ren'), ('copy', 'copy'),
92 ('echo', 'echo'), ('ren', 'ren'), ('copy', 'copy'),
93 ]
93 ]
94 else:
94 else:
95 default_aliases = []
95 default_aliases = []
96
96
97 return default_aliases
97 return default_aliases
98
98
99
99
100 class AliasError(Exception):
100 class AliasError(Exception):
101 pass
101 pass
102
102
103
103
104 class InvalidAliasError(AliasError):
104 class InvalidAliasError(AliasError):
105 pass
105 pass
106
106
107 class AliasCaller(object):
107 class AliasCaller(object):
108 def __init__(self, shell, cmd):
108 def __init__(self, shell, cmd):
109 self.shell = shell
109 self.shell = shell
110 self.cmd = cmd
110 self.cmd = cmd
111 self.nargs = cmd.count('%s')
111 self.nargs = cmd.count('%s')
112 if (self.nargs > 0) and (cmd.find('%l') >= 0):
112 if (self.nargs > 0) and (cmd.find('%l') >= 0):
113 raise InvalidAliasError('The %s and %l specifiers are mutually '
113 raise InvalidAliasError('The %s and %l specifiers are mutually '
114 'exclusive in alias definitions.')
114 'exclusive in alias definitions.')
115
115
116 def __call__(self, rest=''):
116 def __call__(self, rest=''):
117 cmd = self.cmd
117 cmd = self.cmd
118 nargs = self.nargs
118 nargs = self.nargs
119 # Expand the %l special to be the user's input line
119 # Expand the %l special to be the user's input line
120 if cmd.find('%l') >= 0:
120 if cmd.find('%l') >= 0:
121 cmd = cmd.replace('%l', rest)
121 cmd = cmd.replace('%l', rest)
122 rest = ''
122 rest = ''
123 if nargs==0:
123 if nargs==0:
124 # Simple, argument-less aliases
124 # Simple, argument-less aliases
125 cmd = '%s %s' % (cmd, rest)
125 cmd = '%s %s' % (cmd, rest)
126 else:
126 else:
127 # Handle aliases with positional arguments
127 # Handle aliases with positional arguments
128 args = rest.split(None, nargs)
128 args = rest.split(None, nargs)
129 if len(args) < nargs:
129 if len(args) < nargs:
130 raise AliasError('Alias <%s> requires %s arguments, %s given.' %
130 raise AliasError('Alias <%s> requires %s arguments, %s given.' %
131 (alias, nargs, len(args)))
131 (alias, nargs, len(args)))
132 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
132 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
133
133
134 self.shell.system(cmd)
134 self.shell.system(cmd)
135
135
136 #-----------------------------------------------------------------------------
136 #-----------------------------------------------------------------------------
137 # Main AliasManager class
137 # Main AliasManager class
138 #-----------------------------------------------------------------------------
138 #-----------------------------------------------------------------------------
139
139
140 class AliasManager(Configurable):
140 class AliasManager(Configurable):
141
141
142 default_aliases = List(default_aliases(), config=True)
142 default_aliases = List(default_aliases(), config=True)
143 user_aliases = List(default_value=[], config=True)
143 user_aliases = List(default_value=[], config=True)
144 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
144 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
145
145
146 def __init__(self, shell=None, **kwargs):
146 def __init__(self, shell=None, **kwargs):
147 super(AliasManager, self).__init__(shell=shell, **kwargs)
147 super(AliasManager, self).__init__(shell=shell, **kwargs)
148 self.alias_table = {}
148 self.alias_table = {}
149 self.init_exclusions()
149 self.init_exclusions()
150 self.init_aliases()
150 self.init_aliases()
151
151
152 def __contains__(self, name):
152 def __contains__(self, name):
153 return name in self.alias_table
153 return name in self.alias_table
154
154
155 @property
155 @property
156 def aliases(self):
156 def aliases(self):
157 return [(item[0], item[1][1]) for item in self.alias_table.iteritems()]
157 return [(item[0], item[1][1]) for item in self.alias_table.iteritems()]
158
158
159 def init_exclusions(self):
159 def init_exclusions(self):
160 # set of things NOT to alias (keywords, builtins and some magics)
160 # set of things NOT to alias (keywords, builtins and some magics)
161 no_alias = {'cd','popd','pushd','dhist','alias','unalias'}
161 no_alias = {'cd','popd','pushd','dhist','alias','unalias'}
162 no_alias.update(set(keyword.kwlist))
162 no_alias.update(set(keyword.kwlist))
163 no_alias.update(set(__builtin__.__dict__.keys()))
163 no_alias.update(set(__builtin__.__dict__.keys()))
164 self.no_alias = no_alias
164 self.no_alias = no_alias
165
165
166 def init_aliases(self):
166 def init_aliases(self):
167 # Load default aliases
167 # Load default aliases
168 for name, cmd in self.default_aliases:
168 for name, cmd in self.default_aliases:
169 self.soft_define_alias(name, cmd)
169 self.soft_define_alias(name, cmd)
170
170
171 # Load user aliases
171 # Load user aliases
172 for name, cmd in self.user_aliases:
172 for name, cmd in self.user_aliases:
173 self.soft_define_alias(name, cmd)
173 self.soft_define_alias(name, cmd)
174
174
175 def clear_aliases(self):
175 def clear_aliases(self):
176 self.alias_table.clear()
176 self.alias_table.clear()
177
177
178 def soft_define_alias(self, name, cmd):
178 def soft_define_alias(self, name, cmd):
179 """Define an alias, but don't raise on an AliasError."""
179 """Define an alias, but don't raise on an AliasError."""
180 try:
180 try:
181 self.define_alias(name, cmd)
181 self.define_alias(name, cmd)
182 except AliasError as e:
182 except AliasError as e:
183 error("Invalid alias: %s" % e)
183 error("Invalid alias: %s" % e)
184
184
185 def define_alias(self, name, cmd):
185 def define_alias(self, name, cmd):
186 """Define a new alias after validating it.
186 """Define a new alias after validating it.
187
187
188 This will raise an :exc:`AliasError` if there are validation
188 This will raise an :exc:`AliasError` if there are validation
189 problems.
189 problems.
190 """
190 """
191 self.validate_alias(name, cmd)
191 self.validate_alias(name, cmd)
192 caller = AliasCaller(shell=self.shell, cmd=cmd)
192 caller = AliasCaller(shell=self.shell, cmd=cmd)
193 self.shell.magics_manager.register_function(caller, magic_kind='line',
193 self.shell.magics_manager.register_function(caller, magic_kind='line',
194 magic_name=name)
194 magic_name=name)
195
195
196 def undefine_alias(self, name):
196 def undefine_alias(self, name):
197 linemagics = self.shell.magics_manager.magics['line']
197 linemagics = self.shell.magics_manager.magics['line']
198 caller = linemagics.get(name, None)
198 caller = linemagics.get(name, None)
199 if isinstance(caller, AliasCaller):
199 if isinstance(caller, AliasCaller):
200 del linemagics[name]
200 del linemagics[name]
201 else:
201 else:
202 raise ValueError('%s is not an alias' % name)
202 raise ValueError('%s is not an alias' % name)
203
203
204 def validate_alias(self, name, cmd):
204 def validate_alias(self, name, cmd):
205 """Validate an alias and return the its number of arguments."""
205 """Validate an alias and return the its number of arguments."""
206 if name in self.no_alias:
206 if name in self.no_alias:
207 raise InvalidAliasError("The name %s can't be aliased "
207 raise InvalidAliasError("The name %s can't be aliased "
208 "because it is a keyword or builtin." % name)
208 "because it is a keyword or builtin." % name)
209 if not (isinstance(cmd, basestring)):
209 if not (isinstance(cmd, basestring)):
210 raise InvalidAliasError("An alias command must be a string, "
210 raise InvalidAliasError("An alias command must be a string, "
211 "got: %r" % cmd)
211 "got: %r" % cmd)
212 return True
212 return True
213
214 def retrieve_alias(self, name):
215 """Retrieve the command to which an alias expands."""
216 caller = self.shell.magics_manager.magics['line'].get(name, None)
217 if isinstance(caller, AliasCaller):
218 return caller.cmd
219 else:
220 raise ValueError('%s is not an alias' % name)
213
221
214 def call_alias(self, alias, rest=''):
222 def call_alias(self, alias, rest=''):
215 """Call an alias given its name and the rest of the line."""
223 """Call an alias given its name and the rest of the line."""
216 cmd = self.transform_alias(alias, rest)
224 cmd = self.transform_alias(alias, rest)
217 try:
225 try:
218 self.shell.system(cmd)
226 self.shell.system(cmd)
219 except:
227 except:
220 self.shell.showtraceback()
228 self.shell.showtraceback()
221
229
222 def transform_alias(self, alias,rest=''):
230 def transform_alias(self, alias,rest=''):
223 """Transform alias to system command string."""
231 """Transform alias to system command string."""
224 nargs, cmd = self.alias_table[alias]
232 nargs, cmd = self.alias_table[alias]
225
233
226 if ' ' in cmd and os.path.isfile(cmd):
234 if ' ' in cmd and os.path.isfile(cmd):
227 cmd = '"%s"' % cmd
235 cmd = '"%s"' % cmd
228
236
229 # Expand the %l special to be the user's input line
237 # Expand the %l special to be the user's input line
230 if cmd.find('%l') >= 0:
238 if cmd.find('%l') >= 0:
231 cmd = cmd.replace('%l', rest)
239 cmd = cmd.replace('%l', rest)
232 rest = ''
240 rest = ''
233 if nargs==0:
241 if nargs==0:
234 # Simple, argument-less aliases
242 # Simple, argument-less aliases
235 cmd = '%s %s' % (cmd, rest)
243 cmd = '%s %s' % (cmd, rest)
236 else:
244 else:
237 # Handle aliases with positional arguments
245 # Handle aliases with positional arguments
238 args = rest.split(None, nargs)
246 args = rest.split(None, nargs)
239 if len(args) < nargs:
247 if len(args) < nargs:
240 raise AliasError('Alias <%s> requires %s arguments, %s given.' %
248 raise AliasError('Alias <%s> requires %s arguments, %s given.' %
241 (alias, nargs, len(args)))
249 (alias, nargs, len(args)))
242 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
250 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
243 return cmd
251 return cmd
244
252
245 def expand_alias(self, line):
253 def expand_alias(self, line):
246 """ Expand an alias in the command line
254 """ Expand an alias in the command line
247
255
248 Returns the provided command line, possibly with the first word
256 Returns the provided command line, possibly with the first word
249 (command) translated according to alias expansion rules.
257 (command) translated according to alias expansion rules.
250
258
251 [ipython]|16> _ip.expand_aliases("np myfile.txt")
259 [ipython]|16> _ip.expand_aliases("np myfile.txt")
252 <16> 'q:/opt/np/notepad++.exe myfile.txt'
260 <16> 'q:/opt/np/notepad++.exe myfile.txt'
253 """
261 """
254
262
255 pre,_,fn,rest = split_user_input(line)
263 pre,_,fn,rest = split_user_input(line)
256 res = pre + self.expand_aliases(fn, rest)
264 res = pre + self.expand_aliases(fn, rest)
257 return res
265 return res
258
266
259 def expand_aliases(self, fn, rest):
267 def expand_aliases(self, fn, rest):
260 """Expand multiple levels of aliases:
268 """Expand multiple levels of aliases:
261
269
262 if:
270 if:
263
271
264 alias foo bar /tmp
272 alias foo bar /tmp
265 alias baz foo
273 alias baz foo
266
274
267 then:
275 then:
268
276
269 baz huhhahhei -> bar /tmp huhhahhei
277 baz huhhahhei -> bar /tmp huhhahhei
270 """
278 """
271 line = fn + " " + rest
279 line = fn + " " + rest
272
280
273 done = set()
281 done = set()
274 while 1:
282 while 1:
275 pre,_,fn,rest = split_user_input(line, shell_line_split)
283 pre,_,fn,rest = split_user_input(line, shell_line_split)
276 if fn in self.alias_table:
284 if fn in self.alias_table:
277 if fn in done:
285 if fn in done:
278 warn("Cyclic alias definition, repeated '%s'" % fn)
286 warn("Cyclic alias definition, repeated '%s'" % fn)
279 return ""
287 return ""
280 done.add(fn)
288 done.add(fn)
281
289
282 l2 = self.transform_alias(fn, rest)
290 l2 = self.transform_alias(fn, rest)
283 if l2 == line:
291 if l2 == line:
284 break
292 break
285 # ls -> ls -F should not recurse forever
293 # ls -> ls -F should not recurse forever
286 if l2.split(None,1)[0] == line.split(None,1)[0]:
294 if l2.split(None,1)[0] == line.split(None,1)[0]:
287 line = l2
295 line = l2
288 break
296 break
289 line = l2
297 line = l2
290 else:
298 else:
291 break
299 break
292
300
293 return line
301 return line
@@ -1,243 +1,244 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 %store magic for lightweight persistence.
3 %store magic for lightweight persistence.
4
4
5 Stores variables, aliases and macros in IPython's database.
5 Stores variables, aliases and macros in IPython's database.
6
6
7 To automatically restore stored variables at startup, add this to your
7 To automatically restore stored variables at startup, add this to your
8 :file:`ipython_config.py` file::
8 :file:`ipython_config.py` file::
9
9
10 c.StoreMagic.autorestore = True
10 c.StoreMagic.autorestore = True
11 """
11 """
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (c) 2012, The IPython Development Team.
13 # Copyright (c) 2012, The IPython Development Team.
14 #
14 #
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16 #
16 #
17 # The full license is in the file COPYING.txt, distributed with this software.
17 # The full license is in the file COPYING.txt, distributed with this software.
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Imports
21 # Imports
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 # Stdlib
24 # Stdlib
25 import inspect, os, sys, textwrap
25 import inspect, os, sys, textwrap
26
26
27 # Our own
27 # Our own
28 from IPython.config.configurable import Configurable
28 from IPython.config.configurable import Configurable
29 from IPython.core.error import UsageError
29 from IPython.core.error import UsageError
30 from IPython.core.magic import Magics, magics_class, line_magic
30 from IPython.core.magic import Magics, magics_class, line_magic
31 from IPython.testing.skipdoctest import skip_doctest
31 from IPython.testing.skipdoctest import skip_doctest
32 from IPython.utils.traitlets import Bool
32 from IPython.utils.traitlets import Bool
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Functions and classes
35 # Functions and classes
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 def restore_aliases(ip):
38 def restore_aliases(ip):
39 staliases = ip.db.get('stored_aliases', {})
39 staliases = ip.db.get('stored_aliases', {})
40 for k,v in staliases.items():
40 for k,v in staliases.items():
41 #print "restore alias",k,v # dbg
41 #print "restore alias",k,v # dbg
42 #self.alias_table[k] = v
42 #self.alias_table[k] = v
43 ip.alias_manager.define_alias(k,v)
43 ip.alias_manager.define_alias(k,v)
44
44
45
45
46 def refresh_variables(ip):
46 def refresh_variables(ip):
47 db = ip.db
47 db = ip.db
48 for key in db.keys('autorestore/*'):
48 for key in db.keys('autorestore/*'):
49 # strip autorestore
49 # strip autorestore
50 justkey = os.path.basename(key)
50 justkey = os.path.basename(key)
51 try:
51 try:
52 obj = db[key]
52 obj = db[key]
53 except KeyError:
53 except KeyError:
54 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
54 print "Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey
55 print "The error was:", sys.exc_info()[0]
55 print "The error was:", sys.exc_info()[0]
56 else:
56 else:
57 #print "restored",justkey,"=",obj #dbg
57 #print "restored",justkey,"=",obj #dbg
58 ip.user_ns[justkey] = obj
58 ip.user_ns[justkey] = obj
59
59
60
60
61 def restore_dhist(ip):
61 def restore_dhist(ip):
62 ip.user_ns['_dh'] = ip.db.get('dhist',[])
62 ip.user_ns['_dh'] = ip.db.get('dhist',[])
63
63
64
64
65 def restore_data(ip):
65 def restore_data(ip):
66 refresh_variables(ip)
66 refresh_variables(ip)
67 restore_aliases(ip)
67 restore_aliases(ip)
68 restore_dhist(ip)
68 restore_dhist(ip)
69
69
70
70
71 @magics_class
71 @magics_class
72 class StoreMagics(Magics, Configurable):
72 class StoreMagics(Magics, Configurable):
73 """Lightweight persistence for python variables.
73 """Lightweight persistence for python variables.
74
74
75 Provides the %store magic."""
75 Provides the %store magic."""
76
76
77 autorestore = Bool(False, config=True, help=
77 autorestore = Bool(False, config=True, help=
78 """If True, any %store-d variables will be automatically restored
78 """If True, any %store-d variables will be automatically restored
79 when IPython starts.
79 when IPython starts.
80 """
80 """
81 )
81 )
82
82
83 def __init__(self, shell):
83 def __init__(self, shell):
84 Configurable.__init__(self, config=shell.config)
84 Configurable.__init__(self, config=shell.config)
85 Magics.__init__(self, shell=shell)
85 Magics.__init__(self, shell=shell)
86 self.shell.configurables.append(self)
86 self.shell.configurables.append(self)
87 if self.autorestore:
87 if self.autorestore:
88 restore_data(self.shell)
88 restore_data(self.shell)
89
89
90 @skip_doctest
90 @skip_doctest
91 @line_magic
91 @line_magic
92 def store(self, parameter_s=''):
92 def store(self, parameter_s=''):
93 """Lightweight persistence for python variables.
93 """Lightweight persistence for python variables.
94
94
95 Example::
95 Example::
96
96
97 In [1]: l = ['hello',10,'world']
97 In [1]: l = ['hello',10,'world']
98 In [2]: %store l
98 In [2]: %store l
99 In [3]: exit
99 In [3]: exit
100
100
101 (IPython session is closed and started again...)
101 (IPython session is closed and started again...)
102
102
103 ville@badger:~$ ipython
103 ville@badger:~$ ipython
104 In [1]: l
104 In [1]: l
105 NameError: name 'l' is not defined
105 NameError: name 'l' is not defined
106 In [2]: %store -r
106 In [2]: %store -r
107 In [3]: l
107 In [3]: l
108 Out[3]: ['hello', 10, 'world']
108 Out[3]: ['hello', 10, 'world']
109
109
110 Usage:
110 Usage:
111
111
112 * ``%store`` - Show list of all variables and their current
112 * ``%store`` - Show list of all variables and their current
113 values
113 values
114 * ``%store spam`` - Store the *current* value of the variable spam
114 * ``%store spam`` - Store the *current* value of the variable spam
115 to disk
115 to disk
116 * ``%store -d spam`` - Remove the variable and its value from storage
116 * ``%store -d spam`` - Remove the variable and its value from storage
117 * ``%store -z`` - Remove all variables from storage
117 * ``%store -z`` - Remove all variables from storage
118 * ``%store -r`` - Refresh all variables from store (overwrite
118 * ``%store -r`` - Refresh all variables from store (overwrite
119 current vals)
119 current vals)
120 * ``%store -r spam bar`` - Refresh specified variables from store
120 * ``%store -r spam bar`` - Refresh specified variables from store
121 (delete current val)
121 (delete current val)
122 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
122 * ``%store foo >a.txt`` - Store value of foo to new file a.txt
123 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
123 * ``%store foo >>a.txt`` - Append value of foo to file a.txt
124
124
125 It should be noted that if you change the value of a variable, you
125 It should be noted that if you change the value of a variable, you
126 need to %store it again if you want to persist the new value.
126 need to %store it again if you want to persist the new value.
127
127
128 Note also that the variables will need to be pickleable; most basic
128 Note also that the variables will need to be pickleable; most basic
129 python types can be safely %store'd.
129 python types can be safely %store'd.
130
130
131 Also aliases can be %store'd across sessions.
131 Also aliases can be %store'd across sessions.
132 """
132 """
133
133
134 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
134 opts,argsl = self.parse_options(parameter_s,'drz',mode='string')
135 args = argsl.split(None,1)
135 args = argsl.split(None,1)
136 ip = self.shell
136 ip = self.shell
137 db = ip.db
137 db = ip.db
138 # delete
138 # delete
139 if 'd' in opts:
139 if 'd' in opts:
140 try:
140 try:
141 todel = args[0]
141 todel = args[0]
142 except IndexError:
142 except IndexError:
143 raise UsageError('You must provide the variable to forget')
143 raise UsageError('You must provide the variable to forget')
144 else:
144 else:
145 try:
145 try:
146 del db['autorestore/' + todel]
146 del db['autorestore/' + todel]
147 except:
147 except:
148 raise UsageError("Can't delete variable '%s'" % todel)
148 raise UsageError("Can't delete variable '%s'" % todel)
149 # reset
149 # reset
150 elif 'z' in opts:
150 elif 'z' in opts:
151 for k in db.keys('autorestore/*'):
151 for k in db.keys('autorestore/*'):
152 del db[k]
152 del db[k]
153
153
154 elif 'r' in opts:
154 elif 'r' in opts:
155 if args:
155 if args:
156 for arg in args:
156 for arg in args:
157 try:
157 try:
158 obj = db['autorestore/' + arg]
158 obj = db['autorestore/' + arg]
159 except KeyError:
159 except KeyError:
160 print "no stored variable %s" % arg
160 print "no stored variable %s" % arg
161 else:
161 else:
162 ip.user_ns[arg] = obj
162 ip.user_ns[arg] = obj
163 else:
163 else:
164 restore_data(ip)
164 restore_data(ip)
165
165
166 # run without arguments -> list variables & values
166 # run without arguments -> list variables & values
167 elif not args:
167 elif not args:
168 vars = db.keys('autorestore/*')
168 vars = db.keys('autorestore/*')
169 vars.sort()
169 vars.sort()
170 if vars:
170 if vars:
171 size = max(map(len, vars))
171 size = max(map(len, vars))
172 else:
172 else:
173 size = 0
173 size = 0
174
174
175 print 'Stored variables and their in-db values:'
175 print 'Stored variables and their in-db values:'
176 fmt = '%-'+str(size)+'s -> %s'
176 fmt = '%-'+str(size)+'s -> %s'
177 get = db.get
177 get = db.get
178 for var in vars:
178 for var in vars:
179 justkey = os.path.basename(var)
179 justkey = os.path.basename(var)
180 # print 30 first characters from every var
180 # print 30 first characters from every var
181 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
181 print fmt % (justkey, repr(get(var, '<unavailable>'))[:50])
182
182
183 # default action - store the variable
183 # default action - store the variable
184 else:
184 else:
185 # %store foo >file.txt or >>file.txt
185 # %store foo >file.txt or >>file.txt
186 if len(args) > 1 and args[1].startswith('>'):
186 if len(args) > 1 and args[1].startswith('>'):
187 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
187 fnam = os.path.expanduser(args[1].lstrip('>').lstrip())
188 if args[1].startswith('>>'):
188 if args[1].startswith('>>'):
189 fil = open(fnam, 'a')
189 fil = open(fnam, 'a')
190 else:
190 else:
191 fil = open(fnam, 'w')
191 fil = open(fnam, 'w')
192 obj = ip.ev(args[0])
192 obj = ip.ev(args[0])
193 print "Writing '%s' (%s) to file '%s'." % (args[0],
193 print "Writing '%s' (%s) to file '%s'." % (args[0],
194 obj.__class__.__name__, fnam)
194 obj.__class__.__name__, fnam)
195
195
196
196
197 if not isinstance (obj, basestring):
197 if not isinstance (obj, basestring):
198 from pprint import pprint
198 from pprint import pprint
199 pprint(obj, fil)
199 pprint(obj, fil)
200 else:
200 else:
201 fil.write(obj)
201 fil.write(obj)
202 if not obj.endswith('\n'):
202 if not obj.endswith('\n'):
203 fil.write('\n')
203 fil.write('\n')
204
204
205 fil.close()
205 fil.close()
206 return
206 return
207
207
208 # %store foo
208 # %store foo
209 try:
209 try:
210 obj = ip.user_ns[args[0]]
210 obj = ip.user_ns[args[0]]
211 except KeyError:
211 except KeyError:
212 # it might be an alias
212 # it might be an alias
213 # This needs to be refactored to use the new AliasManager stuff.
213 # This needs to be refactored to use the new AliasManager stuff.
214 if args[0] in ip.alias_manager:
214 name = args[0]
215 name = args[0]
215 try:
216 nargs, cmd = ip.alias_manager.alias_table[ name ]
216 cmd = ip.alias_manager.retrieve_alias(name)
217 staliases = db.get('stored_aliases',{})
217 except ValueError:
218 staliases[ name ] = cmd
218 raise UsageError("Unknown variable '%s'" % name)
219 db['stored_aliases'] = staliases
219
220 print "Alias stored: %s (%s)" % (name, cmd)
220 staliases = db.get('stored_aliases',{})
221 return
221 staliases[name] = cmd
222 else:
222 db['stored_aliases'] = staliases
223 raise UsageError("Unknown variable '%s'" % args[0])
223 print "Alias stored: %s (%s)" % (name, cmd)
224 return
224
225
225 else:
226 else:
226 modname = getattr(inspect.getmodule(obj), '__name__', '')
227 modname = getattr(inspect.getmodule(obj), '__name__', '')
227 if modname == '__main__':
228 if modname == '__main__':
228 print textwrap.dedent("""\
229 print textwrap.dedent("""\
229 Warning:%s is %s
230 Warning:%s is %s
230 Proper storage of interactively declared classes (or instances
231 Proper storage of interactively declared classes (or instances
231 of those classes) is not possible! Only instances
232 of those classes) is not possible! Only instances
232 of classes in real modules on file system can be %%store'd.
233 of classes in real modules on file system can be %%store'd.
233 """ % (args[0], obj) )
234 """ % (args[0], obj) )
234 return
235 return
235 #pickled = pickle.dumps(obj)
236 #pickled = pickle.dumps(obj)
236 db[ 'autorestore/' + args[0] ] = obj
237 db[ 'autorestore/' + args[0] ] = obj
237 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
238 print "Stored '%s' (%s)" % (args[0], obj.__class__.__name__)
238
239
239
240
240 def load_ipython_extension(ip):
241 def load_ipython_extension(ip):
241 """Load the extension in IPython."""
242 """Load the extension in IPython."""
242 ip.register_magics(StoreMagics)
243 ip.register_magics(StoreMagics)
243
244
General Comments 0
You need to be logged in to leave comments. Login now