##// END OF EJS Templates
AliasCaller knows its own name
Thomas Kluyver -
Show More
@@ -1,220 +1,225 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.error import UsageError
30 from IPython.core.splitinput import split_user_input
31 from IPython.core.splitinput import split_user_input
31
32
32 from IPython.utils.traitlets import List, Instance
33 from IPython.utils.traitlets import List, Instance
33 from IPython.utils.warn import warn, error
34 from IPython.utils.warn import warn, error
34
35
35 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
36 # Utilities
37 # Utilities
37 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
38
39
39 # This is used as the pattern for calls to split_user_input.
40 # This is used as the pattern for calls to split_user_input.
40 shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)')
41 shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)')
41
42
42 def default_aliases():
43 def default_aliases():
43 """Return list of shell aliases to auto-define.
44 """Return list of shell aliases to auto-define.
44 """
45 """
45 # Note: the aliases defined here should be safe to use on a kernel
46 # 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
47 # 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
48 # kernel in-process can define additional aliases that will only work in
48 # their case. For example, things like 'less' or 'clear' that manipulate
49 # 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
50 # 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.
51 # kernel is running inside a true terminal, and not over the network.
51
52
52 if os.name == 'posix':
53 if os.name == 'posix':
53 default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
54 default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
54 ('mv', 'mv -i'), ('rm', 'rm -i'), ('cp', 'cp -i'),
55 ('mv', 'mv -i'), ('rm', 'rm -i'), ('cp', 'cp -i'),
55 ('cat', 'cat'),
56 ('cat', 'cat'),
56 ]
57 ]
57 # Useful set of ls aliases. The GNU and BSD options are a little
58 # 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
59 # different, so we make aliases that provide as similar as possible
59 # behavior in ipython, by passing the right flags for each platform
60 # behavior in ipython, by passing the right flags for each platform
60 if sys.platform.startswith('linux'):
61 if sys.platform.startswith('linux'):
61 ls_aliases = [('ls', 'ls -F --color'),
62 ls_aliases = [('ls', 'ls -F --color'),
62 # long ls
63 # long ls
63 ('ll', 'ls -F -o --color'),
64 ('ll', 'ls -F -o --color'),
64 # ls normal files only
65 # ls normal files only
65 ('lf', 'ls -F -o --color %l | grep ^-'),
66 ('lf', 'ls -F -o --color %l | grep ^-'),
66 # ls symbolic links
67 # ls symbolic links
67 ('lk', 'ls -F -o --color %l | grep ^l'),
68 ('lk', 'ls -F -o --color %l | grep ^l'),
68 # directories or links to directories,
69 # directories or links to directories,
69 ('ldir', 'ls -F -o --color %l | grep /$'),
70 ('ldir', 'ls -F -o --color %l | grep /$'),
70 # things which are executable
71 # things which are executable
71 ('lx', 'ls -F -o --color %l | grep ^-..x'),
72 ('lx', 'ls -F -o --color %l | grep ^-..x'),
72 ]
73 ]
73 else:
74 else:
74 # BSD, OSX, etc.
75 # BSD, OSX, etc.
75 ls_aliases = [('ls', 'ls -F -G'),
76 ls_aliases = [('ls', 'ls -F -G'),
76 # long ls
77 # long ls
77 ('ll', 'ls -F -l -G'),
78 ('ll', 'ls -F -l -G'),
78 # ls normal files only
79 # ls normal files only
79 ('lf', 'ls -F -l -G %l | grep ^-'),
80 ('lf', 'ls -F -l -G %l | grep ^-'),
80 # ls symbolic links
81 # ls symbolic links
81 ('lk', 'ls -F -l -G %l | grep ^l'),
82 ('lk', 'ls -F -l -G %l | grep ^l'),
82 # directories or links to directories,
83 # directories or links to directories,
83 ('ldir', 'ls -F -G -l %l | grep /$'),
84 ('ldir', 'ls -F -G -l %l | grep /$'),
84 # things which are executable
85 # things which are executable
85 ('lx', 'ls -F -l -G %l | grep ^-..x'),
86 ('lx', 'ls -F -l -G %l | grep ^-..x'),
86 ]
87 ]
87 default_aliases = default_aliases + ls_aliases
88 default_aliases = default_aliases + ls_aliases
88 elif os.name in ['nt', 'dos']:
89 elif os.name in ['nt', 'dos']:
89 default_aliases = [('ls', 'dir /on'),
90 default_aliases = [('ls', 'dir /on'),
90 ('ddir', 'dir /ad /on'), ('ldir', 'dir /ad /on'),
91 ('ddir', 'dir /ad /on'), ('ldir', 'dir /ad /on'),
91 ('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
92 ('mkdir', 'mkdir'), ('rmdir', 'rmdir'),
92 ('echo', 'echo'), ('ren', 'ren'), ('copy', 'copy'),
93 ('echo', 'echo'), ('ren', 'ren'), ('copy', 'copy'),
93 ]
94 ]
94 else:
95 else:
95 default_aliases = []
96 default_aliases = []
96
97
97 return default_aliases
98 return default_aliases
98
99
99
100
100 class AliasError(Exception):
101 class AliasError(Exception):
101 pass
102 pass
102
103
103
104
104 class InvalidAliasError(AliasError):
105 class InvalidAliasError(AliasError):
105 pass
106 pass
106
107
107 class AliasCaller(object):
108 class AliasCaller(object):
108 def __init__(self, shell, cmd):
109 def __init__(self, shell, name, cmd):
109 self.shell = shell
110 self.shell = shell
111 self.name = name
110 self.cmd = cmd
112 self.cmd = cmd
111 self.nargs = cmd.count('%s')
113 self.nargs = cmd.count('%s')
112 if (self.nargs > 0) and (cmd.find('%l') >= 0):
114 if (self.nargs > 0) and (cmd.find('%l') >= 0):
113 raise InvalidAliasError('The %s and %l specifiers are mutually '
115 raise InvalidAliasError('The %s and %l specifiers are mutually '
114 'exclusive in alias definitions.')
116 'exclusive in alias definitions.')
117
118 def __repr__(self):
119 return "<alias {} for {!r}>".format(self.name, self.cmd)
115
120
116 def __call__(self, rest=''):
121 def __call__(self, rest=''):
117 cmd = self.cmd
122 cmd = self.cmd
118 nargs = self.nargs
123 nargs = self.nargs
119 # Expand the %l special to be the user's input line
124 # Expand the %l special to be the user's input line
120 if cmd.find('%l') >= 0:
125 if cmd.find('%l') >= 0:
121 cmd = cmd.replace('%l', rest)
126 cmd = cmd.replace('%l', rest)
122 rest = ''
127 rest = ''
123 if nargs==0:
128 if nargs==0:
124 # Simple, argument-less aliases
129 # Simple, argument-less aliases
125 cmd = '%s %s' % (cmd, rest)
130 cmd = '%s %s' % (cmd, rest)
126 else:
131 else:
127 # Handle aliases with positional arguments
132 # Handle aliases with positional arguments
128 args = rest.split(None, nargs)
133 args = rest.split(None, nargs)
129 if len(args) < nargs:
134 if len(args) < nargs:
130 raise AliasError('Alias <%s> requires %s arguments, %s given.' %
135 raise UsageError('Alias <%s> requires %s arguments, %s given.' %
131 (alias, nargs, len(args)))
136 (self.name, nargs, len(args)))
132 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
137 cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:]))
133
138
134 self.shell.system(cmd)
139 self.shell.system(cmd)
135
140
136 #-----------------------------------------------------------------------------
141 #-----------------------------------------------------------------------------
137 # Main AliasManager class
142 # Main AliasManager class
138 #-----------------------------------------------------------------------------
143 #-----------------------------------------------------------------------------
139
144
140 class AliasManager(Configurable):
145 class AliasManager(Configurable):
141
146
142 default_aliases = List(default_aliases(), config=True)
147 default_aliases = List(default_aliases(), config=True)
143 user_aliases = List(default_value=[], config=True)
148 user_aliases = List(default_value=[], config=True)
144 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
149 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
145
150
146 def __init__(self, shell=None, **kwargs):
151 def __init__(self, shell=None, **kwargs):
147 super(AliasManager, self).__init__(shell=shell, **kwargs)
152 super(AliasManager, self).__init__(shell=shell, **kwargs)
148 self.alias_table = {}
153 self.alias_table = {}
149 self.init_exclusions()
154 self.init_exclusions()
150 self.init_aliases()
155 self.init_aliases()
151
156
152 @property
157 @property
153 def aliases(self):
158 def aliases(self):
154 linemagics = self.shell.magics_manager.magics['line']
159 linemagics = self.shell.magics_manager.magics['line']
155 return [(n, func.cmd) for (n, func) in linemagics.items()
160 return [(n, func.cmd) for (n, func) in linemagics.items()
156 if isinstance(func, AliasCaller)]
161 if isinstance(func, AliasCaller)]
157
162
158 def init_exclusions(self):
163 def init_exclusions(self):
159 # set of things NOT to alias (keywords, builtins and some magics)
164 # set of things NOT to alias (keywords, builtins and some magics)
160 no_alias = {'cd','popd','pushd','dhist','alias','unalias'}
165 no_alias = {'cd','popd','pushd','dhist','alias','unalias'}
161 no_alias.update(set(keyword.kwlist))
166 no_alias.update(set(keyword.kwlist))
162 no_alias.update(set(__builtin__.__dict__.keys()))
167 no_alias.update(set(__builtin__.__dict__.keys()))
163 self.no_alias = no_alias
168 self.no_alias = no_alias
164
169
165 def init_aliases(self):
170 def init_aliases(self):
166 # Load default aliases
171 # Load default aliases
167 for name, cmd in self.default_aliases:
172 for name, cmd in self.default_aliases:
168 self.soft_define_alias(name, cmd)
173 self.soft_define_alias(name, cmd)
169
174
170 # Load user aliases
175 # Load user aliases
171 for name, cmd in self.user_aliases:
176 for name, cmd in self.user_aliases:
172 self.soft_define_alias(name, cmd)
177 self.soft_define_alias(name, cmd)
173
178
174 def clear_aliases(self):
179 def clear_aliases(self):
175 for name, cmd in self.aliases:
180 for name, cmd in self.aliases:
176 self.undefine_alias(name)
181 self.undefine_alias(name)
177
182
178 def soft_define_alias(self, name, cmd):
183 def soft_define_alias(self, name, cmd):
179 """Define an alias, but don't raise on an AliasError."""
184 """Define an alias, but don't raise on an AliasError."""
180 try:
185 try:
181 self.define_alias(name, cmd)
186 self.define_alias(name, cmd)
182 except AliasError as e:
187 except AliasError as e:
183 error("Invalid alias: %s" % e)
188 error("Invalid alias: %s" % e)
184
189
185 def define_alias(self, name, cmd):
190 def define_alias(self, name, cmd):
186 """Define a new alias after validating it.
191 """Define a new alias after validating it.
187
192
188 This will raise an :exc:`AliasError` if there are validation
193 This will raise an :exc:`AliasError` if there are validation
189 problems.
194 problems.
190 """
195 """
191 self.validate_alias(name, cmd)
196 self.validate_alias(name, cmd)
192 caller = AliasCaller(shell=self.shell, cmd=cmd)
197 caller = AliasCaller(shell=self.shell, name=name, cmd=cmd)
193 self.shell.magics_manager.register_function(caller, magic_kind='line',
198 self.shell.magics_manager.register_function(caller, magic_kind='line',
194 magic_name=name)
199 magic_name=name)
195
200
196 def undefine_alias(self, name):
201 def undefine_alias(self, name):
197 linemagics = self.shell.magics_manager.magics['line']
202 linemagics = self.shell.magics_manager.magics['line']
198 caller = linemagics.get(name, None)
203 caller = linemagics.get(name, None)
199 if isinstance(caller, AliasCaller):
204 if isinstance(caller, AliasCaller):
200 del linemagics[name]
205 del linemagics[name]
201 else:
206 else:
202 raise ValueError('%s is not an alias' % name)
207 raise ValueError('%s is not an alias' % name)
203
208
204 def validate_alias(self, name, cmd):
209 def validate_alias(self, name, cmd):
205 """Validate an alias and return the its number of arguments."""
210 """Validate an alias and return the its number of arguments."""
206 if name in self.no_alias:
211 if name in self.no_alias:
207 raise InvalidAliasError("The name %s can't be aliased "
212 raise InvalidAliasError("The name %s can't be aliased "
208 "because it is a keyword or builtin." % name)
213 "because it is a keyword or builtin." % name)
209 if not (isinstance(cmd, basestring)):
214 if not (isinstance(cmd, basestring)):
210 raise InvalidAliasError("An alias command must be a string, "
215 raise InvalidAliasError("An alias command must be a string, "
211 "got: %r" % cmd)
216 "got: %r" % cmd)
212 return True
217 return True
213
218
214 def retrieve_alias(self, name):
219 def retrieve_alias(self, name):
215 """Retrieve the command to which an alias expands."""
220 """Retrieve the command to which an alias expands."""
216 caller = self.shell.magics_manager.magics['line'].get(name, None)
221 caller = self.shell.magics_manager.magics['line'].get(name, None)
217 if isinstance(caller, AliasCaller):
222 if isinstance(caller, AliasCaller):
218 return caller.cmd
223 return caller.cmd
219 else:
224 else:
220 raise ValueError('%s is not an alias' % name)
225 raise ValueError('%s is not an alias' % name)
General Comments 0
You need to be logged in to leave comments. Login now