Show More
@@ -0,0 +1,41 b'' | |||
|
1 | from IPython.utils.capture import capture_output | |
|
2 | ||
|
3 | import nose.tools as nt | |
|
4 | ||
|
5 | def test_alias_lifecycle(): | |
|
6 | name = 'test_alias1' | |
|
7 | cmd = 'echo "Hello"' | |
|
8 | am = _ip.alias_manager | |
|
9 | am.clear_aliases() | |
|
10 | am.define_alias(name, cmd) | |
|
11 | assert am.is_alias(name) | |
|
12 | nt.assert_equal(am.retrieve_alias(name), cmd) | |
|
13 | nt.assert_in((name, cmd), am.aliases) | |
|
14 | ||
|
15 | # Test running the alias | |
|
16 | orig_system = _ip.system | |
|
17 | result = [] | |
|
18 | _ip.system = result.append | |
|
19 | try: | |
|
20 | _ip.run_cell('%{}'.format(name)) | |
|
21 | result = [c.strip() for c in result] | |
|
22 | nt.assert_equal(result, [cmd]) | |
|
23 | finally: | |
|
24 | _ip.system = orig_system | |
|
25 | ||
|
26 | # Test removing the alias | |
|
27 | am.undefine_alias(name) | |
|
28 | assert not am.is_alias(name) | |
|
29 | with nt.assert_raises(ValueError): | |
|
30 | am.retrieve_alias(name) | |
|
31 | nt.assert_not_in((name, cmd), am.aliases) | |
|
32 | ||
|
33 | ||
|
34 | def test_alias_args_error(): | |
|
35 | """Error expanding with wrong number of arguments""" | |
|
36 | _ip.alias_manager.define_alias('parts', 'echo first %s second %s') | |
|
37 | # capture stderr: | |
|
38 | with capture_output() as cap: | |
|
39 | _ip.run_cell('parts 1') | |
|
40 | ||
|
41 | nt.assert_equal(cap.stderr.split(':')[0], 'UsageError') No newline at end of file |
@@ -0,0 +1,3 b'' | |||
|
1 | - The alias system has been reimplemented to use magic functions. There should be little | |
|
2 | visible difference while automagics are enabled, as they are by default, but parts of the | |
|
3 | :class:`~IPython.core.alias.AliasManager` API have been removed. |
@@ -20,17 +20,15 b' Authors:' | |||
|
20 | 20 | # Imports |
|
21 | 21 | #----------------------------------------------------------------------------- |
|
22 | 22 | |
|
23 | import __builtin__ | |
|
24 | import keyword | |
|
25 | 23 | import os |
|
26 | 24 | import re |
|
27 | 25 | import sys |
|
28 | 26 | |
|
29 | 27 | from IPython.config.configurable import Configurable |
|
30 |
from IPython.core. |
|
|
28 | from IPython.core.error import UsageError | |
|
31 | 29 | |
|
32 | 30 | from IPython.utils.traitlets import List, Instance |
|
33 |
from IPython.utils.warn import |
|
|
31 | from IPython.utils.warn import error | |
|
34 | 32 | |
|
35 | 33 | #----------------------------------------------------------------------------- |
|
36 | 34 | # Utilities |
@@ -104,97 +102,53 b' class AliasError(Exception):' | |||
|
104 | 102 | class InvalidAliasError(AliasError): |
|
105 | 103 | pass |
|
106 | 104 | |
|
107 | #----------------------------------------------------------------------------- | |
|
108 | # Main AliasManager class | |
|
109 | #----------------------------------------------------------------------------- | |
|
110 | ||
|
111 | class AliasManager(Configurable): | |
|
112 | ||
|
113 | default_aliases = List(default_aliases(), config=True) | |
|
114 | user_aliases = List(default_value=[], config=True) | |
|
115 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
|
116 | ||
|
117 | def __init__(self, shell=None, **kwargs): | |
|
118 | super(AliasManager, self).__init__(shell=shell, **kwargs) | |
|
119 | self.alias_table = {} | |
|
120 | self.exclude_aliases() | |
|
121 | self.init_aliases() | |
|
122 | ||
|
123 | def __contains__(self, name): | |
|
124 | return name in self.alias_table | |
|
125 | ||
|
126 | @property | |
|
127 | def aliases(self): | |
|
128 | return [(item[0], item[1][1]) for item in self.alias_table.iteritems()] | |
|
129 | ||
|
130 | def exclude_aliases(self): | |
|
131 | # set of things NOT to alias (keywords, builtins and some magics) | |
|
132 | no_alias = set(['cd','popd','pushd','dhist','alias','unalias']) | |
|
133 | no_alias.update(set(keyword.kwlist)) | |
|
134 | no_alias.update(set(__builtin__.__dict__.keys())) | |
|
135 | self.no_alias = no_alias | |
|
105 | class Alias(object): | |
|
106 | """Callable object storing the details of one alias. | |
|
136 | 107 |
|
|
137 | def init_aliases(self): | |
|
138 | # Load default aliases | |
|
139 | for name, cmd in self.default_aliases: | |
|
140 | self.soft_define_alias(name, cmd) | |
|
108 | Instances are registered as magic functions to allow use of aliases. | |
|
109 | """ | |
|
141 | 110 | |
|
142 | # Load user aliases | |
|
143 | for name, cmd in self.user_aliases: | |
|
144 | self.soft_define_alias(name, cmd) | |
|
111 | # Prepare blacklist | |
|
112 | blacklist = {'cd','popd','pushd','dhist','alias','unalias'} | |
|
145 | 113 | |
|
146 | def clear_aliases(self): | |
|
147 | self.alias_table.clear() | |
|
114 | def __init__(self, shell, name, cmd): | |
|
115 | self.shell = shell | |
|
116 | self.name = name | |
|
117 | self.cmd = cmd | |
|
118 | self.nargs = self.validate() | |
|
148 | 119 | |
|
149 | def soft_define_alias(self, name, cmd): | |
|
150 | """Define an alias, but don't raise on an AliasError.""" | |
|
120 | def validate(self): | |
|
121 | """Validate the alias, and return the number of arguments.""" | |
|
122 | if self.name in self.blacklist: | |
|
123 | raise InvalidAliasError("The name %s can't be aliased " | |
|
124 | "because it is a keyword or builtin." % self.name) | |
|
151 | 125 | try: |
|
152 | self.define_alias(name, cmd) | |
|
153 |
except |
|
|
154 | error("Invalid alias: %s" % e) | |
|
155 | ||
|
156 | def define_alias(self, name, cmd): | |
|
157 | """Define a new alias after validating it. | |
|
126 | caller = self.shell.magics_manager.magics['line'][self.name] | |
|
127 | except KeyError: | |
|
128 | pass | |
|
129 | else: | |
|
130 | if not isinstance(caller, Alias): | |
|
131 | raise InvalidAliasError("The name %s can't be aliased " | |
|
132 | "because it is another magic command." % self.name) | |
|
158 | 133 | |
|
159 | This will raise an :exc:`AliasError` if there are validation | |
|
160 | problems. | |
|
161 | """ | |
|
162 | nargs = self.validate_alias(name, cmd) | |
|
163 | self.alias_table[name] = (nargs, cmd) | |
|
134 | if not (isinstance(self.cmd, basestring)): | |
|
135 | raise InvalidAliasError("An alias command must be a string, " | |
|
136 | "got: %r" % self.cmd) | |
|
164 | 137 | |
|
165 | def undefine_alias(self, name): | |
|
166 | if name in self.alias_table: | |
|
167 | del self.alias_table[name] | |
|
138 | nargs = self.cmd.count('%s') | |
|
168 | 139 | |
|
169 | def validate_alias(self, name, cmd): | |
|
170 | """Validate an alias and return the its number of arguments.""" | |
|
171 | if name in self.no_alias: | |
|
172 | raise InvalidAliasError("The name %s can't be aliased " | |
|
173 | "because it is a keyword or builtin." % name) | |
|
174 | if not (isinstance(cmd, basestring)): | |
|
175 | raise InvalidAliasError("An alias command must be a string, " | |
|
176 | "got: %r" % cmd) | |
|
177 | nargs = cmd.count('%s') | |
|
178 | if nargs>0 and cmd.find('%l')>=0: | |
|
140 | if (nargs > 0) and (self.cmd.find('%l') >= 0): | |
|
179 | 141 | raise InvalidAliasError('The %s and %l specifiers are mutually ' |
|
180 | 142 | 'exclusive in alias definitions.') |
|
181 | return nargs | |
|
182 | ||
|
183 | def call_alias(self, alias, rest=''): | |
|
184 | """Call an alias given its name and the rest of the line.""" | |
|
185 | cmd = self.transform_alias(alias, rest) | |
|
186 | try: | |
|
187 | self.shell.system(cmd) | |
|
188 | except: | |
|
189 | self.shell.showtraceback() | |
|
190 | 143 | |
|
191 | def transform_alias(self, alias,rest=''): | |
|
192 | """Transform alias to system command string.""" | |
|
193 | nargs, cmd = self.alias_table[alias] | |
|
144 | return nargs | |
|
194 | 145 | |
|
195 | if ' ' in cmd and os.path.isfile(cmd): | |
|
196 | cmd = '"%s"' % cmd | |
|
146 | def __repr__(self): | |
|
147 | return "<alias {} for {!r}>".format(self.name, self.cmd) | |
|
197 | 148 | |
|
149 | def __call__(self, rest=''): | |
|
150 | cmd = self.cmd | |
|
151 | nargs = self.nargs | |
|
198 | 152 | # Expand the %l special to be the user's input line |
|
199 | 153 | if cmd.find('%l') >= 0: |
|
200 | 154 | cmd = cmd.replace('%l', rest) |
@@ -206,57 +160,78 b' class AliasManager(Configurable):' | |||
|
206 | 160 | # Handle aliases with positional arguments |
|
207 | 161 | args = rest.split(None, nargs) |
|
208 | 162 | if len(args) < nargs: |
|
209 |
raise |
|
|
210 |
( |
|
|
163 | raise UsageError('Alias <%s> requires %s arguments, %s given.' % | |
|
164 | (self.name, nargs, len(args))) | |
|
211 | 165 | cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:])) |
|
212 | return cmd | |
|
213 | 166 | |
|
214 | def expand_alias(self, line): | |
|
215 | """ Expand an alias in the command line | |
|
167 | self.shell.system(cmd) | |
|
168 | ||
|
169 | #----------------------------------------------------------------------------- | |
|
170 | # Main AliasManager class | |
|
171 | #----------------------------------------------------------------------------- | |
|
216 | 172 | |
|
217 | Returns the provided command line, possibly with the first word | |
|
218 | (command) translated according to alias expansion rules. | |
|
173 | class AliasManager(Configurable): | |
|
219 | 174 | |
|
220 | [ipython]|16> _ip.expand_aliases("np myfile.txt") | |
|
221 | <16> 'q:/opt/np/notepad++.exe myfile.txt' | |
|
222 | """ | |
|
175 | default_aliases = List(default_aliases(), config=True) | |
|
176 | user_aliases = List(default_value=[], config=True) | |
|
177 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | |
|
223 | 178 | |
|
224 | pre,_,fn,rest = split_user_input(line) | |
|
225 | res = pre + self.expand_aliases(fn, rest) | |
|
226 | return res | |
|
179 | def __init__(self, shell=None, **kwargs): | |
|
180 | super(AliasManager, self).__init__(shell=shell, **kwargs) | |
|
181 | # For convenient access | |
|
182 | self.linemagics = self.shell.magics_manager.magics['line'] | |
|
183 | self.init_aliases() | |
|
227 | 184 | |
|
228 |
def |
|
|
229 | """Expand multiple levels of aliases: | |
|
185 | def init_aliases(self): | |
|
186 | # Load default & user aliases | |
|
187 | for name, cmd in self.default_aliases + self.user_aliases: | |
|
188 | self.soft_define_alias(name, cmd) | |
|
230 | 189 | |
|
231 | if: | |
|
190 | @property | |
|
191 | def aliases(self): | |
|
192 | return [(n, func.cmd) for (n, func) in self.linemagics.items() | |
|
193 | if isinstance(func, Alias)] | |
|
232 | 194 | |
|
233 | alias foo bar /tmp | |
|
234 | alias baz foo | |
|
195 | def soft_define_alias(self, name, cmd): | |
|
196 | """Define an alias, but don't raise on an AliasError.""" | |
|
197 | try: | |
|
198 | self.define_alias(name, cmd) | |
|
199 | except AliasError as e: | |
|
200 | error("Invalid alias: %s" % e) | |
|
235 | 201 | |
|
236 | then: | |
|
202 | def define_alias(self, name, cmd): | |
|
203 | """Define a new alias after validating it. | |
|
237 | 204 | |
|
238 | baz huhhahhei -> bar /tmp huhhahhei | |
|
205 | This will raise an :exc:`AliasError` if there are validation | |
|
206 | problems. | |
|
239 | 207 | """ |
|
240 | line = fn + " " + rest | |
|
241 | ||
|
242 | done = set() | |
|
243 | while 1: | |
|
244 | pre,_,fn,rest = split_user_input(line, shell_line_split) | |
|
245 | if fn in self.alias_table: | |
|
246 | if fn in done: | |
|
247 | warn("Cyclic alias definition, repeated '%s'" % fn) | |
|
248 | return "" | |
|
249 | done.add(fn) | |
|
250 | ||
|
251 | l2 = self.transform_alias(fn, rest) | |
|
252 | if l2 == line: | |
|
253 | break | |
|
254 | # ls -> ls -F should not recurse forever | |
|
255 | if l2.split(None,1)[0] == line.split(None,1)[0]: | |
|
256 | line = l2 | |
|
257 | break | |
|
258 | line = l2 | |
|
208 | caller = Alias(shell=self.shell, name=name, cmd=cmd) | |
|
209 | self.shell.magics_manager.register_function(caller, magic_kind='line', | |
|
210 | magic_name=name) | |
|
211 | ||
|
212 | def get_alias(self, name): | |
|
213 | """Return an alias, or None if no alias by that name exists.""" | |
|
214 | aname = self.linemagics.get(name, None) | |
|
215 | return aname if isinstance(aname, Alias) else None | |
|
216 | ||
|
217 | def is_alias(self, name): | |
|
218 | """Return whether or not a given name has been defined as an alias""" | |
|
219 | return self.get_alias(name) is not None | |
|
220 | ||
|
221 | def undefine_alias(self, name): | |
|
222 | if self.is_alias(name): | |
|
223 | del self.linemagics[name] | |
|
259 | 224 |
|
|
260 | break | |
|
225 | raise ValueError('%s is not an alias' % name) | |
|
261 | 226 | |
|
262 | return line | |
|
227 | def clear_aliases(self): | |
|
228 | for name, cmd in self.aliases: | |
|
229 | self.undefine_alias(name) | |
|
230 | ||
|
231 | def retrieve_alias(self, name): | |
|
232 | """Retrieve the command to which an alias expands.""" | |
|
233 | caller = self.get_alias(name) | |
|
234 | if caller: | |
|
235 | return caller.cmd | |
|
236 | else: | |
|
237 | raise ValueError('%s is not an alias' % name) |
@@ -431,8 +431,7 b' class IPCompleter(Completer):' | |||
|
431 | 431 | ) |
|
432 | 432 | |
|
433 | 433 | def __init__(self, shell=None, namespace=None, global_namespace=None, |
|
434 |
|
|
|
435 | config=None, **kwargs): | |
|
434 | use_readline=True, config=None, **kwargs): | |
|
436 | 435 | """IPCompleter() -> completer |
|
437 | 436 | |
|
438 | 437 | Return a completer object suitable for use by the readline library |
@@ -450,9 +449,6 b' class IPCompleter(Completer):' | |||
|
450 | 449 | handle cases (such as IPython embedded inside functions) where |
|
451 | 450 | both Python scopes are visible. |
|
452 | 451 | |
|
453 | - If alias_table is supplied, it should be a dictionary of aliases | |
|
454 | to complete. | |
|
455 | ||
|
456 | 452 | use_readline : bool, optional |
|
457 | 453 | If true, use the readline library. This completer can still function |
|
458 | 454 | without readline, though in that case callers must provide some extra |
@@ -476,9 +472,6 b' class IPCompleter(Completer):' | |||
|
476 | 472 | # List where completion matches will be stored |
|
477 | 473 | self.matches = [] |
|
478 | 474 | self.shell = shell |
|
479 | if alias_table is None: | |
|
480 | alias_table = {} | |
|
481 | self.alias_table = alias_table | |
|
482 | 475 | # Regexp to split filenames with spaces in them |
|
483 | 476 | self.space_name_re = re.compile(r'([^\\] )') |
|
484 | 477 | # Hold a local ref. to glob.glob for speed |
@@ -505,7 +498,6 b' class IPCompleter(Completer):' | |||
|
505 | 498 | self.matchers = [self.python_matches, |
|
506 | 499 | self.file_matches, |
|
507 | 500 | self.magic_matches, |
|
508 | self.alias_matches, | |
|
509 | 501 | self.python_func_kw_matches, |
|
510 | 502 | ] |
|
511 | 503 | |
@@ -628,22 +620,6 b' class IPCompleter(Completer):' | |||
|
628 | 620 | comp += [ pre+m for m in line_magics if m.startswith(bare_text)] |
|
629 | 621 | return comp |
|
630 | 622 | |
|
631 | def alias_matches(self, text): | |
|
632 | """Match internal system aliases""" | |
|
633 | #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg | |
|
634 | ||
|
635 | # if we are not in the first 'item', alias matching | |
|
636 | # doesn't make sense - unless we are starting with 'sudo' command. | |
|
637 | main_text = self.text_until_cursor.lstrip() | |
|
638 | if ' ' in main_text and not main_text.startswith('sudo'): | |
|
639 | return [] | |
|
640 | text = os.path.expanduser(text) | |
|
641 | aliases = self.alias_table.keys() | |
|
642 | if text == '': | |
|
643 | return aliases | |
|
644 | else: | |
|
645 | return [a for a in aliases if a.startswith(text)] | |
|
646 | ||
|
647 | 623 | def python_matches(self,text): |
|
648 | 624 | """Match attributes or global python names""" |
|
649 | 625 |
@@ -470,7 +470,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
470 | 470 | # because it and init_io have to come after init_readline. |
|
471 | 471 | self.init_user_ns() |
|
472 | 472 | self.init_logger() |
|
473 | self.init_alias() | |
|
474 | 473 | self.init_builtins() |
|
475 | 474 | |
|
476 | 475 | # The following was in post_config_initialization |
@@ -502,6 +501,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
502 | 501 | self.init_displayhook() |
|
503 | 502 | self.init_latextool() |
|
504 | 503 | self.init_magics() |
|
504 | self.init_alias() | |
|
505 | 505 | self.init_logstart() |
|
506 | 506 | self.init_pdb() |
|
507 | 507 | self.init_extension_manager() |
@@ -1363,9 +1363,7 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1363 | 1363 | namespaces = [ ('Interactive', self.user_ns), |
|
1364 | 1364 | ('Interactive (global)', self.user_global_ns), |
|
1365 | 1365 | ('Python builtin', builtin_mod.__dict__), |
|
1366 | ('Alias', self.alias_manager.alias_table), | |
|
1367 | 1366 | ] |
|
1368 | alias_ns = self.alias_manager.alias_table | |
|
1369 | 1367 | |
|
1370 | 1368 | # initialize results to 'null' |
|
1371 | 1369 | found = False; obj = None; ospace = None; ds = None; |
@@ -1404,8 +1402,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1404 | 1402 | # If we finish the for loop (no break), we got all members |
|
1405 | 1403 | found = True |
|
1406 | 1404 | ospace = nsname |
|
1407 | if ns == alias_ns: | |
|
1408 | isalias = True | |
|
1409 | 1405 | break # namespace loop |
|
1410 | 1406 | |
|
1411 | 1407 | # Try to see if it's magic |
@@ -1940,7 +1936,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1940 | 1936 | self.Completer = IPCompleter(shell=self, |
|
1941 | 1937 | namespace=self.user_ns, |
|
1942 | 1938 | global_namespace=self.user_global_ns, |
|
1943 | alias_table=self.alias_manager.alias_table, | |
|
1944 | 1939 | use_readline=self.has_readline, |
|
1945 | 1940 | parent=self, |
|
1946 | 1941 | ) |
@@ -2305,7 +2300,6 b' class InteractiveShell(SingletonConfigurable):' | |||
|
2305 | 2300 | def init_alias(self): |
|
2306 | 2301 | self.alias_manager = AliasManager(shell=self, parent=self) |
|
2307 | 2302 | self.configurables.append(self.alias_manager) |
|
2308 | self.ns_table['alias'] = self.alias_manager.alias_table, | |
|
2309 | 2303 | |
|
2310 | 2304 | #------------------------------------------------------------------------- |
|
2311 | 2305 | # Things related to extensions |
@@ -26,6 +26,7 b' from pprint import pformat' | |||
|
26 | 26 | from IPython.core import magic_arguments |
|
27 | 27 | from IPython.core import oinspect |
|
28 | 28 | from IPython.core import page |
|
29 | from IPython.core.alias import AliasError, Alias | |
|
29 | 30 | from IPython.core.error import UsageError |
|
30 | 31 | from IPython.core.magic import ( |
|
31 | 32 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic |
@@ -113,10 +114,14 b' class OSMagics(Magics):' | |||
|
113 | 114 | # Now try to define a new one |
|
114 | 115 | try: |
|
115 | 116 | alias,cmd = par.split(None, 1) |
|
116 | except: | |
|
117 |
print |
|
|
118 | else: | |
|
119 | self.shell.alias_manager.soft_define_alias(alias, cmd) | |
|
117 | except TypeError: | |
|
118 | print(oinspect.getdoc(self.alias)) | |
|
119 | return | |
|
120 | ||
|
121 | try: | |
|
122 | self.shell.alias_manager.define_alias(alias, cmd) | |
|
123 | except AliasError as e: | |
|
124 | print(e) | |
|
120 | 125 | # end magic_alias |
|
121 | 126 | |
|
122 | 127 | @line_magic |
@@ -124,7 +129,12 b' class OSMagics(Magics):' | |||
|
124 | 129 | """Remove an alias""" |
|
125 | 130 | |
|
126 | 131 | aname = parameter_s.strip() |
|
132 | try: | |
|
127 | 133 | self.shell.alias_manager.undefine_alias(aname) |
|
134 | except ValueError as e: | |
|
135 | print(e) | |
|
136 | return | |
|
137 | ||
|
128 | 138 | stored = self.shell.db.get('stored_aliases', {} ) |
|
129 | 139 | if aname in stored: |
|
130 | 140 | print "Removing %stored alias",aname |
@@ -182,7 +192,7 b' class OSMagics(Magics):' | |||
|
182 | 192 | try: |
|
183 | 193 | # Removes dots from the name since ipython |
|
184 | 194 | # will assume names with dots to be python. |
|
185 |
if |
|
|
195 | if not self.shell.alias_manager.is_alias(ff): | |
|
186 | 196 | self.shell.alias_manager.define_alias( |
|
187 | 197 | ff.replace('.',''), ff) |
|
188 | 198 | except InvalidAliasError: |
@@ -190,7 +200,7 b' class OSMagics(Magics):' | |||
|
190 | 200 | else: |
|
191 | 201 | syscmdlist.append(ff) |
|
192 | 202 | else: |
|
193 |
no_alias = s |
|
|
203 | no_alias = Alias.blacklist | |
|
194 | 204 | for pdir in path: |
|
195 | 205 | os.chdir(pdir) |
|
196 | 206 | for ff in os.listdir(pdir): |
@@ -488,22 +488,6 b' class AutoMagicChecker(PrefilterChecker):' | |||
|
488 | 488 | return self.prefilter_manager.get_handler_by_name('magic') |
|
489 | 489 | |
|
490 | 490 | |
|
491 | class AliasChecker(PrefilterChecker): | |
|
492 | ||
|
493 | priority = Integer(800, config=True) | |
|
494 | ||
|
495 | def check(self, line_info): | |
|
496 | "Check if the initital identifier on the line is an alias." | |
|
497 | # Note: aliases can not contain '.' | |
|
498 | head = line_info.ifun.split('.',1)[0] | |
|
499 | if line_info.ifun not in self.shell.alias_manager \ | |
|
500 | or head not in self.shell.alias_manager \ | |
|
501 | or is_shadowed(head, self.shell): | |
|
502 | return None | |
|
503 | ||
|
504 | return self.prefilter_manager.get_handler_by_name('alias') | |
|
505 | ||
|
506 | ||
|
507 | 491 | class PythonOpsChecker(PrefilterChecker): |
|
508 | 492 | |
|
509 | 493 | priority = Integer(900, config=True) |
@@ -591,20 +575,6 b' class PrefilterHandler(Configurable):' | |||
|
591 | 575 | return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name) |
|
592 | 576 | |
|
593 | 577 | |
|
594 | class AliasHandler(PrefilterHandler): | |
|
595 | ||
|
596 | handler_name = Unicode('alias') | |
|
597 | ||
|
598 | def handle(self, line_info): | |
|
599 | """Handle alias input lines. """ | |
|
600 | transformed = self.shell.alias_manager.expand_aliases(line_info.ifun,line_info.the_rest) | |
|
601 | # pre is needed, because it carries the leading whitespace. Otherwise | |
|
602 | # aliases won't work in indented sections. | |
|
603 | line_out = '%sget_ipython().system(%r)' % (line_info.pre_whitespace, transformed) | |
|
604 | ||
|
605 | return line_out | |
|
606 | ||
|
607 | ||
|
608 | 578 | class MacroHandler(PrefilterHandler): |
|
609 | 579 | handler_name = Unicode("macro") |
|
610 | 580 | |
@@ -730,14 +700,12 b' _default_checkers = [' | |||
|
730 | 700 | IPyAutocallChecker, |
|
731 | 701 | AssignmentChecker, |
|
732 | 702 | AutoMagicChecker, |
|
733 | AliasChecker, | |
|
734 | 703 | PythonOpsChecker, |
|
735 | 704 | AutocallChecker |
|
736 | 705 | ] |
|
737 | 706 | |
|
738 | 707 | _default_handlers = [ |
|
739 | 708 | PrefilterHandler, |
|
740 | AliasHandler, | |
|
741 | 709 | MacroHandler, |
|
742 | 710 | MagicHandler, |
|
743 | 711 | AutoHandler, |
@@ -45,28 +45,6 b' def run(tests):' | |||
|
45 | 45 | |
|
46 | 46 | |
|
47 | 47 | def test_handlers(): |
|
48 | # alias expansion | |
|
49 | ||
|
50 | # We're using 'true' as our syscall of choice because it doesn't | |
|
51 | # write anything to stdout. | |
|
52 | ||
|
53 | # Turn off actual execution of aliases, because it's noisy | |
|
54 | old_system_cmd = ip.system | |
|
55 | ip.system = lambda cmd: None | |
|
56 | ||
|
57 | ||
|
58 | ip.alias_manager.alias_table['an_alias'] = (0, 'true') | |
|
59 | # These are useful for checking a particular recursive alias issue | |
|
60 | ip.alias_manager.alias_table['top'] = (0, 'd:/cygwin/top') | |
|
61 | ip.alias_manager.alias_table['d'] = (0, 'true') | |
|
62 | run([(i,py3compat.u_format(o)) for i,o in \ | |
|
63 | [("an_alias", "get_ipython().system({u}'true ')"), # alias | |
|
64 | # Below: recursive aliases should expand whitespace-surrounded | |
|
65 | # chars, *not* initial chars which happen to be aliases: | |
|
66 | ("top", "get_ipython().system({u}'d:/cygwin/top ')"), | |
|
67 | ]]) | |
|
68 | ip.system = old_system_cmd | |
|
69 | ||
|
70 | 48 | call_idx = CallableIndexable() |
|
71 | 49 | ip.user_ns['call_idx'] = call_idx |
|
72 | 50 |
@@ -106,17 +106,6 b' class InteractiveShellTestCase(unittest.TestCase):' | |||
|
106 | 106 | ip.run_cell('a = """\n%exit\n"""') |
|
107 | 107 | self.assertEqual(ip.user_ns['a'], '\n%exit\n') |
|
108 | 108 | |
|
109 | def test_alias_crash(self): | |
|
110 | """Errors in prefilter can't crash IPython""" | |
|
111 | ip.run_cell('%alias parts echo first %s second %s') | |
|
112 | # capture stderr: | |
|
113 | save_err = io.stderr | |
|
114 | io.stderr = StringIO() | |
|
115 | ip.run_cell('parts 1') | |
|
116 | err = io.stderr.getvalue() | |
|
117 | io.stderr = save_err | |
|
118 | self.assertEqual(err.split(':')[0], 'ERROR') | |
|
119 | ||
|
120 | 109 | def test_trailing_newline(self): |
|
121 | 110 | """test that running !(command) does not raise a SyntaxError""" |
|
122 | 111 | ip.run_cell('!(true)\n', False) |
@@ -48,16 +48,16 b' class DummyMagics(magic.Magics): pass' | |||
|
48 | 48 | def test_rehashx(): |
|
49 | 49 | # clear up everything |
|
50 | 50 | _ip = get_ipython() |
|
51 |
_ip.alias_manager. |
|
|
51 | _ip.alias_manager.clear_aliases() | |
|
52 | 52 | del _ip.db['syscmdlist'] |
|
53 | 53 | |
|
54 | 54 | _ip.magic('rehashx') |
|
55 | 55 | # Practically ALL ipython development systems will have more than 10 aliases |
|
56 | 56 | |
|
57 |
nt.assert_true(len(_ip.alias_manager.alias |
|
|
58 |
for |
|
|
57 | nt.assert_true(len(_ip.alias_manager.aliases) > 10) | |
|
58 | for name, cmd in _ip.alias_manager.aliases: | |
|
59 | 59 | # we must strip dots from alias names |
|
60 |
nt.assert_not_in('.', |
|
|
60 | nt.assert_not_in('.', name) | |
|
61 | 61 | |
|
62 | 62 | # rehashx must fill up syscmdlist |
|
63 | 63 | scoms = _ip.db['syscmdlist'] |
@@ -210,17 +210,17 b' class StoreMagics(Magics, Configurable):' | |||
|
210 | 210 | obj = ip.user_ns[args[0]] |
|
211 | 211 | except KeyError: |
|
212 | 212 | # it might be an alias |
|
213 | # This needs to be refactored to use the new AliasManager stuff. | |
|
214 | if args[0] in ip.alias_manager: | |
|
215 | 213 |
|
|
216 | nargs, cmd = ip.alias_manager.alias_table[ name ] | |
|
214 | try: | |
|
215 | cmd = ip.alias_manager.retrieve_alias(name) | |
|
216 | except ValueError: | |
|
217 | raise UsageError("Unknown variable '%s'" % name) | |
|
218 | ||
|
217 | 219 |
|
|
218 | 220 |
|
|
219 | 221 |
|
|
220 | 222 |
|
|
221 | 223 |
|
|
222 | else: | |
|
223 | raise UsageError("Unknown variable '%s'" % args[0]) | |
|
224 | 224 | |
|
225 | 225 | else: |
|
226 | 226 | modname = getattr(inspect.getmodule(obj), '__name__', '') |
@@ -27,7 +27,7 b' def test_store_restore():' | |||
|
27 | 27 | # Check restoring |
|
28 | 28 | ip.magic('store -r') |
|
29 | 29 | nt.assert_equal(ip.user_ns['foo'], 78) |
|
30 |
|
|
|
30 | assert ip.alias_manager.is_alias('bar') | |
|
31 | 31 | nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh']) |
|
32 | 32 | |
|
33 | 33 | os.rmdir(tmpd) |
@@ -369,7 +369,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
369 | 369 | |
|
370 | 370 | |
|
371 | 371 | for name, cmd in aliases: |
|
372 | self.alias_manager.define_alias(name, cmd) | |
|
372 | self.alias_manager.soft_define_alias(name, cmd) | |
|
373 | 373 | |
|
374 | 374 | #------------------------------------------------------------------------- |
|
375 | 375 | # Things related to the banner and usage |
General Comments 0
You need to be logged in to leave comments.
Login now