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