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 | # Imports |
|
20 | # Imports | |
21 | #----------------------------------------------------------------------------- |
|
21 | #----------------------------------------------------------------------------- | |
22 |
|
22 | |||
23 | import __builtin__ |
|
|||
24 | import keyword |
|
|||
25 | import os |
|
23 | import os | |
26 | import re |
|
24 | import re | |
27 | import sys |
|
25 | import sys | |
28 |
|
26 | |||
29 | from IPython.config.configurable import Configurable |
|
27 | from IPython.config.configurable import Configurable | |
30 |
from IPython.core. |
|
28 | from IPython.core.error import UsageError | |
31 |
|
29 | |||
32 | from IPython.utils.traitlets import List, Instance |
|
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 | # Utilities |
|
34 | # Utilities | |
@@ -104,6 +102,70 b' class AliasError(Exception):' | |||||
104 | class InvalidAliasError(AliasError): |
|
102 | class InvalidAliasError(AliasError): | |
105 | pass |
|
103 | pass | |
106 |
|
104 | |||
|
105 | class Alias(object): | |||
|
106 | """Callable object storing the details of one alias. | |||
|
107 | ||||
|
108 | Instances are registered as magic functions to allow use of aliases. | |||
|
109 | """ | |||
|
110 | ||||
|
111 | # Prepare blacklist | |||
|
112 | blacklist = {'cd','popd','pushd','dhist','alias','unalias'} | |||
|
113 | ||||
|
114 | def __init__(self, shell, name, cmd): | |||
|
115 | self.shell = shell | |||
|
116 | self.name = name | |||
|
117 | self.cmd = cmd | |||
|
118 | self.nargs = self.validate() | |||
|
119 | ||||
|
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) | |||
|
125 | try: | |||
|
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) | |||
|
133 | ||||
|
134 | if not (isinstance(self.cmd, basestring)): | |||
|
135 | raise InvalidAliasError("An alias command must be a string, " | |||
|
136 | "got: %r" % self.cmd) | |||
|
137 | ||||
|
138 | nargs = self.cmd.count('%s') | |||
|
139 | ||||
|
140 | if (nargs > 0) and (self.cmd.find('%l') >= 0): | |||
|
141 | raise InvalidAliasError('The %s and %l specifiers are mutually ' | |||
|
142 | 'exclusive in alias definitions.') | |||
|
143 | ||||
|
144 | return nargs | |||
|
145 | ||||
|
146 | def __repr__(self): | |||
|
147 | return "<alias {} for {!r}>".format(self.name, self.cmd) | |||
|
148 | ||||
|
149 | def __call__(self, rest=''): | |||
|
150 | cmd = self.cmd | |||
|
151 | nargs = self.nargs | |||
|
152 | # Expand the %l special to be the user's input line | |||
|
153 | if cmd.find('%l') >= 0: | |||
|
154 | cmd = cmd.replace('%l', rest) | |||
|
155 | rest = '' | |||
|
156 | if nargs==0: | |||
|
157 | # Simple, argument-less aliases | |||
|
158 | cmd = '%s %s' % (cmd, rest) | |||
|
159 | else: | |||
|
160 | # Handle aliases with positional arguments | |||
|
161 | args = rest.split(None, nargs) | |||
|
162 | if len(args) < nargs: | |||
|
163 | raise UsageError('Alias <%s> requires %s arguments, %s given.' % | |||
|
164 | (self.name, nargs, len(args))) | |||
|
165 | cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:])) | |||
|
166 | ||||
|
167 | self.shell.system(cmd) | |||
|
168 | ||||
107 | #----------------------------------------------------------------------------- |
|
169 | #----------------------------------------------------------------------------- | |
108 | # Main AliasManager class |
|
170 | # Main AliasManager class | |
109 | #----------------------------------------------------------------------------- |
|
171 | #----------------------------------------------------------------------------- | |
@@ -116,35 +178,19 b' class AliasManager(Configurable):' | |||||
116 |
|
178 | |||
117 | def __init__(self, shell=None, **kwargs): |
|
179 | def __init__(self, shell=None, **kwargs): | |
118 | super(AliasManager, self).__init__(shell=shell, **kwargs) |
|
180 | super(AliasManager, self).__init__(shell=shell, **kwargs) | |
119 | self.alias_table = {} |
|
181 | # For convenient access | |
120 | self.exclude_aliases() |
|
182 | self.linemagics = self.shell.magics_manager.magics['line'] | |
121 | self.init_aliases() |
|
183 | self.init_aliases() | |
122 |
|
184 | |||
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 |
|
|||
136 |
|
||||
137 | def init_aliases(self): |
|
185 | def init_aliases(self): | |
138 | # Load default aliases |
|
186 | # Load default & user aliases | |
139 | for name, cmd in self.default_aliases: |
|
187 | for name, cmd in self.default_aliases + self.user_aliases: | |
140 | self.soft_define_alias(name, cmd) |
|
|||
141 |
|
||||
142 | # Load user aliases |
|
|||
143 | for name, cmd in self.user_aliases: |
|
|||
144 | self.soft_define_alias(name, cmd) |
|
188 | self.soft_define_alias(name, cmd) | |
145 |
|
189 | |||
146 | def clear_aliases(self): |
|
190 | @property | |
147 | self.alias_table.clear() |
|
191 | def aliases(self): | |
|
192 | return [(n, func.cmd) for (n, func) in self.linemagics.items() | |||
|
193 | if isinstance(func, Alias)] | |||
148 |
|
194 | |||
149 | def soft_define_alias(self, name, cmd): |
|
195 | def soft_define_alias(self, name, cmd): | |
150 | """Define an alias, but don't raise on an AliasError.""" |
|
196 | """Define an alias, but don't raise on an AliasError.""" | |
@@ -159,104 +205,33 b' class AliasManager(Configurable):' | |||||
159 | This will raise an :exc:`AliasError` if there are validation |
|
205 | This will raise an :exc:`AliasError` if there are validation | |
160 | problems. |
|
206 | problems. | |
161 | """ |
|
207 | """ | |
162 | nargs = self.validate_alias(name, cmd) |
|
208 | caller = Alias(shell=self.shell, name=name, cmd=cmd) | |
163 | self.alias_table[name] = (nargs, cmd) |
|
209 | self.shell.magics_manager.register_function(caller, magic_kind='line', | |
|
210 | magic_name=name) | |||
164 |
|
211 | |||
165 |
def |
|
212 | def get_alias(self, name): | |
166 | if name in self.alias_table: |
|
213 | """Return an alias, or None if no alias by that name exists.""" | |
167 | del self.alias_table[name] |
|
214 | aname = self.linemagics.get(name, None) | |
|
215 | return aname if isinstance(aname, Alias) else None | |||
168 |
|
216 | |||
169 |
def |
|
217 | def is_alias(self, name): | |
170 | """Validate an alias and return the its number of arguments.""" |
|
218 | """Return whether or not a given name has been defined as an alias""" | |
171 | if name in self.no_alias: |
|
219 | return self.get_alias(name) is not None | |
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: |
|
|||
179 | raise InvalidAliasError('The %s and %l specifiers are mutually ' |
|
|||
180 | 'exclusive in alias definitions.') |
|
|||
181 | return nargs |
|
|||
182 |
|
220 | |||
183 |
def |
|
221 | def undefine_alias(self, name): | |
184 | """Call an alias given its name and the rest of the line.""" |
|
222 | if self.is_alias(name): | |
185 | cmd = self.transform_alias(alias, rest) |
|
223 | del self.linemagics[name] | |
186 | try: |
|
|||
187 | self.shell.system(cmd) |
|
|||
188 | except: |
|
|||
189 | self.shell.showtraceback() |
|
|||
190 |
|
||||
191 | def transform_alias(self, alias,rest=''): |
|
|||
192 | """Transform alias to system command string.""" |
|
|||
193 | nargs, cmd = self.alias_table[alias] |
|
|||
194 |
|
||||
195 | if ' ' in cmd and os.path.isfile(cmd): |
|
|||
196 | cmd = '"%s"' % cmd |
|
|||
197 |
|
||||
198 | # Expand the %l special to be the user's input line |
|
|||
199 | if cmd.find('%l') >= 0: |
|
|||
200 | cmd = cmd.replace('%l', rest) |
|
|||
201 | rest = '' |
|
|||
202 | if nargs==0: |
|
|||
203 | # Simple, argument-less aliases |
|
|||
204 | cmd = '%s %s' % (cmd, rest) |
|
|||
205 | else: |
|
224 | else: | |
206 | # Handle aliases with positional arguments |
|
225 | raise ValueError('%s is not an alias' % name) | |
207 | args = rest.split(None, nargs) |
|
|||
208 | if len(args) < nargs: |
|
|||
209 | raise AliasError('Alias <%s> requires %s arguments, %s given.' % |
|
|||
210 | (alias, nargs, len(args))) |
|
|||
211 | cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:])) |
|
|||
212 | return cmd |
|
|||
213 |
|
||||
214 | def expand_alias(self, line): |
|
|||
215 | """ Expand an alias in the command line |
|
|||
216 |
|
226 | |||
217 | Returns the provided command line, possibly with the first word |
|
227 | def clear_aliases(self): | |
218 | (command) translated according to alias expansion rules. |
|
228 | for name, cmd in self.aliases: | |
219 |
|
229 | self.undefine_alias(name) | ||
220 | [ipython]|16> _ip.expand_aliases("np myfile.txt") |
|
230 | ||
221 | <16> 'q:/opt/np/notepad++.exe myfile.txt' |
|
231 | def retrieve_alias(self, name): | |
222 | """ |
|
232 | """Retrieve the command to which an alias expands.""" | |
223 |
|
233 | caller = self.get_alias(name) | ||
224 | pre,_,fn,rest = split_user_input(line) |
|
234 | if caller: | |
225 | res = pre + self.expand_aliases(fn, rest) |
|
235 | return caller.cmd | |
226 |
|
|
236 | else: | |
227 |
|
237 | raise ValueError('%s is not an alias' % name) | ||
228 | def expand_aliases(self, fn, rest): |
|
|||
229 | """Expand multiple levels of aliases: |
|
|||
230 |
|
||||
231 | if: |
|
|||
232 |
|
||||
233 | alias foo bar /tmp |
|
|||
234 | alias baz foo |
|
|||
235 |
|
||||
236 | then: |
|
|||
237 |
|
||||
238 | baz huhhahhei -> bar /tmp huhhahhei |
|
|||
239 | """ |
|
|||
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 |
|
|||
259 | else: |
|
|||
260 | break |
|
|||
261 |
|
||||
262 | return line |
|
@@ -431,8 +431,7 b' class IPCompleter(Completer):' | |||||
431 | ) |
|
431 | ) | |
432 |
|
432 | |||
433 | def __init__(self, shell=None, namespace=None, global_namespace=None, |
|
433 | def __init__(self, shell=None, namespace=None, global_namespace=None, | |
434 |
|
|
434 | use_readline=True, config=None, **kwargs): | |
435 | config=None, **kwargs): |
|
|||
436 | """IPCompleter() -> completer |
|
435 | """IPCompleter() -> completer | |
437 |
|
436 | |||
438 | Return a completer object suitable for use by the readline library |
|
437 | Return a completer object suitable for use by the readline library | |
@@ -450,9 +449,6 b' class IPCompleter(Completer):' | |||||
450 | handle cases (such as IPython embedded inside functions) where |
|
449 | handle cases (such as IPython embedded inside functions) where | |
451 | both Python scopes are visible. |
|
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 | use_readline : bool, optional |
|
452 | use_readline : bool, optional | |
457 | If true, use the readline library. This completer can still function |
|
453 | If true, use the readline library. This completer can still function | |
458 | without readline, though in that case callers must provide some extra |
|
454 | without readline, though in that case callers must provide some extra | |
@@ -476,9 +472,6 b' class IPCompleter(Completer):' | |||||
476 | # List where completion matches will be stored |
|
472 | # List where completion matches will be stored | |
477 | self.matches = [] |
|
473 | self.matches = [] | |
478 | self.shell = shell |
|
474 | self.shell = shell | |
479 | if alias_table is None: |
|
|||
480 | alias_table = {} |
|
|||
481 | self.alias_table = alias_table |
|
|||
482 | # Regexp to split filenames with spaces in them |
|
475 | # Regexp to split filenames with spaces in them | |
483 | self.space_name_re = re.compile(r'([^\\] )') |
|
476 | self.space_name_re = re.compile(r'([^\\] )') | |
484 | # Hold a local ref. to glob.glob for speed |
|
477 | # Hold a local ref. to glob.glob for speed | |
@@ -505,7 +498,6 b' class IPCompleter(Completer):' | |||||
505 | self.matchers = [self.python_matches, |
|
498 | self.matchers = [self.python_matches, | |
506 | self.file_matches, |
|
499 | self.file_matches, | |
507 | self.magic_matches, |
|
500 | self.magic_matches, | |
508 | self.alias_matches, |
|
|||
509 | self.python_func_kw_matches, |
|
501 | self.python_func_kw_matches, | |
510 | ] |
|
502 | ] | |
511 |
|
503 | |||
@@ -628,22 +620,6 b' class IPCompleter(Completer):' | |||||
628 | comp += [ pre+m for m in line_magics if m.startswith(bare_text)] |
|
620 | comp += [ pre+m for m in line_magics if m.startswith(bare_text)] | |
629 | return comp |
|
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 | def python_matches(self,text): |
|
623 | def python_matches(self,text): | |
648 | """Match attributes or global python names""" |
|
624 | """Match attributes or global python names""" | |
649 |
|
625 |
@@ -470,7 +470,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
470 | # because it and init_io have to come after init_readline. |
|
470 | # because it and init_io have to come after init_readline. | |
471 | self.init_user_ns() |
|
471 | self.init_user_ns() | |
472 | self.init_logger() |
|
472 | self.init_logger() | |
473 | self.init_alias() |
|
|||
474 | self.init_builtins() |
|
473 | self.init_builtins() | |
475 |
|
474 | |||
476 | # The following was in post_config_initialization |
|
475 | # The following was in post_config_initialization | |
@@ -502,6 +501,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
502 | self.init_displayhook() |
|
501 | self.init_displayhook() | |
503 | self.init_latextool() |
|
502 | self.init_latextool() | |
504 | self.init_magics() |
|
503 | self.init_magics() | |
|
504 | self.init_alias() | |||
505 | self.init_logstart() |
|
505 | self.init_logstart() | |
506 | self.init_pdb() |
|
506 | self.init_pdb() | |
507 | self.init_extension_manager() |
|
507 | self.init_extension_manager() | |
@@ -1363,9 +1363,7 b' class InteractiveShell(SingletonConfigurable):' | |||||
1363 | namespaces = [ ('Interactive', self.user_ns), |
|
1363 | namespaces = [ ('Interactive', self.user_ns), | |
1364 | ('Interactive (global)', self.user_global_ns), |
|
1364 | ('Interactive (global)', self.user_global_ns), | |
1365 | ('Python builtin', builtin_mod.__dict__), |
|
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 | # initialize results to 'null' |
|
1368 | # initialize results to 'null' | |
1371 | found = False; obj = None; ospace = None; ds = None; |
|
1369 | found = False; obj = None; ospace = None; ds = None; | |
@@ -1404,8 +1402,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
1404 | # If we finish the for loop (no break), we got all members |
|
1402 | # If we finish the for loop (no break), we got all members | |
1405 | found = True |
|
1403 | found = True | |
1406 | ospace = nsname |
|
1404 | ospace = nsname | |
1407 | if ns == alias_ns: |
|
|||
1408 | isalias = True |
|
|||
1409 | break # namespace loop |
|
1405 | break # namespace loop | |
1410 |
|
1406 | |||
1411 | # Try to see if it's magic |
|
1407 | # Try to see if it's magic | |
@@ -1940,7 +1936,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
1940 | self.Completer = IPCompleter(shell=self, |
|
1936 | self.Completer = IPCompleter(shell=self, | |
1941 | namespace=self.user_ns, |
|
1937 | namespace=self.user_ns, | |
1942 | global_namespace=self.user_global_ns, |
|
1938 | global_namespace=self.user_global_ns, | |
1943 | alias_table=self.alias_manager.alias_table, |
|
|||
1944 | use_readline=self.has_readline, |
|
1939 | use_readline=self.has_readline, | |
1945 | parent=self, |
|
1940 | parent=self, | |
1946 | ) |
|
1941 | ) | |
@@ -2305,7 +2300,6 b' class InteractiveShell(SingletonConfigurable):' | |||||
2305 | def init_alias(self): |
|
2300 | def init_alias(self): | |
2306 | self.alias_manager = AliasManager(shell=self, parent=self) |
|
2301 | self.alias_manager = AliasManager(shell=self, parent=self) | |
2307 | self.configurables.append(self.alias_manager) |
|
2302 | self.configurables.append(self.alias_manager) | |
2308 | self.ns_table['alias'] = self.alias_manager.alias_table, |
|
|||
2309 |
|
2303 | |||
2310 | #------------------------------------------------------------------------- |
|
2304 | #------------------------------------------------------------------------- | |
2311 | # Things related to extensions |
|
2305 | # Things related to extensions |
@@ -26,6 +26,7 b' from pprint import pformat' | |||||
26 | from IPython.core import magic_arguments |
|
26 | from IPython.core import magic_arguments | |
27 | from IPython.core import oinspect |
|
27 | from IPython.core import oinspect | |
28 | from IPython.core import page |
|
28 | from IPython.core import page | |
|
29 | from IPython.core.alias import AliasError, Alias | |||
29 | from IPython.core.error import UsageError |
|
30 | from IPython.core.error import UsageError | |
30 | from IPython.core.magic import ( |
|
31 | from IPython.core.magic import ( | |
31 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic |
|
32 | Magics, compress_dhist, magics_class, line_magic, cell_magic, line_cell_magic | |
@@ -113,10 +114,14 b' class OSMagics(Magics):' | |||||
113 | # Now try to define a new one |
|
114 | # Now try to define a new one | |
114 | try: |
|
115 | try: | |
115 | alias,cmd = par.split(None, 1) |
|
116 | alias,cmd = par.split(None, 1) | |
116 | except: |
|
117 | except TypeError: | |
117 |
print |
|
118 | print(oinspect.getdoc(self.alias)) | |
118 | else: |
|
119 | return | |
119 | self.shell.alias_manager.soft_define_alias(alias, cmd) |
|
120 | ||
|
121 | try: | |||
|
122 | self.shell.alias_manager.define_alias(alias, cmd) | |||
|
123 | except AliasError as e: | |||
|
124 | print(e) | |||
120 | # end magic_alias |
|
125 | # end magic_alias | |
121 |
|
126 | |||
122 | @line_magic |
|
127 | @line_magic | |
@@ -124,7 +129,12 b' class OSMagics(Magics):' | |||||
124 | """Remove an alias""" |
|
129 | """Remove an alias""" | |
125 |
|
130 | |||
126 | aname = parameter_s.strip() |
|
131 | aname = parameter_s.strip() | |
127 | self.shell.alias_manager.undefine_alias(aname) |
|
132 | try: | |
|
133 | self.shell.alias_manager.undefine_alias(aname) | |||
|
134 | except ValueError as e: | |||
|
135 | print(e) | |||
|
136 | return | |||
|
137 | ||||
128 | stored = self.shell.db.get('stored_aliases', {} ) |
|
138 | stored = self.shell.db.get('stored_aliases', {} ) | |
129 | if aname in stored: |
|
139 | if aname in stored: | |
130 | print "Removing %stored alias",aname |
|
140 | print "Removing %stored alias",aname | |
@@ -182,7 +192,7 b' class OSMagics(Magics):' | |||||
182 | try: |
|
192 | try: | |
183 | # Removes dots from the name since ipython |
|
193 | # Removes dots from the name since ipython | |
184 | # will assume names with dots to be python. |
|
194 | # will assume names with dots to be python. | |
185 |
if |
|
195 | if not self.shell.alias_manager.is_alias(ff): | |
186 | self.shell.alias_manager.define_alias( |
|
196 | self.shell.alias_manager.define_alias( | |
187 | ff.replace('.',''), ff) |
|
197 | ff.replace('.',''), ff) | |
188 | except InvalidAliasError: |
|
198 | except InvalidAliasError: | |
@@ -190,7 +200,7 b' class OSMagics(Magics):' | |||||
190 | else: |
|
200 | else: | |
191 | syscmdlist.append(ff) |
|
201 | syscmdlist.append(ff) | |
192 | else: |
|
202 | else: | |
193 |
no_alias = s |
|
203 | no_alias = Alias.blacklist | |
194 | for pdir in path: |
|
204 | for pdir in path: | |
195 | os.chdir(pdir) |
|
205 | os.chdir(pdir) | |
196 | for ff in os.listdir(pdir): |
|
206 | for ff in os.listdir(pdir): |
@@ -488,22 +488,6 b' class AutoMagicChecker(PrefilterChecker):' | |||||
488 | return self.prefilter_manager.get_handler_by_name('magic') |
|
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 | class PythonOpsChecker(PrefilterChecker): |
|
491 | class PythonOpsChecker(PrefilterChecker): | |
508 |
|
492 | |||
509 | priority = Integer(900, config=True) |
|
493 | priority = Integer(900, config=True) | |
@@ -591,20 +575,6 b' class PrefilterHandler(Configurable):' | |||||
591 | return "<%s(name=%s)>" % (self.__class__.__name__, self.handler_name) |
|
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 | class MacroHandler(PrefilterHandler): |
|
578 | class MacroHandler(PrefilterHandler): | |
609 | handler_name = Unicode("macro") |
|
579 | handler_name = Unicode("macro") | |
610 |
|
580 | |||
@@ -730,14 +700,12 b' _default_checkers = [' | |||||
730 | IPyAutocallChecker, |
|
700 | IPyAutocallChecker, | |
731 | AssignmentChecker, |
|
701 | AssignmentChecker, | |
732 | AutoMagicChecker, |
|
702 | AutoMagicChecker, | |
733 | AliasChecker, |
|
|||
734 | PythonOpsChecker, |
|
703 | PythonOpsChecker, | |
735 | AutocallChecker |
|
704 | AutocallChecker | |
736 | ] |
|
705 | ] | |
737 |
|
706 | |||
738 | _default_handlers = [ |
|
707 | _default_handlers = [ | |
739 | PrefilterHandler, |
|
708 | PrefilterHandler, | |
740 | AliasHandler, |
|
|||
741 | MacroHandler, |
|
709 | MacroHandler, | |
742 | MagicHandler, |
|
710 | MagicHandler, | |
743 | AutoHandler, |
|
711 | AutoHandler, |
@@ -45,28 +45,6 b' def run(tests):' | |||||
45 |
|
45 | |||
46 |
|
46 | |||
47 | def test_handlers(): |
|
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 | call_idx = CallableIndexable() |
|
48 | call_idx = CallableIndexable() | |
71 | ip.user_ns['call_idx'] = call_idx |
|
49 | ip.user_ns['call_idx'] = call_idx | |
72 |
|
50 |
@@ -106,17 +106,6 b' class InteractiveShellTestCase(unittest.TestCase):' | |||||
106 | ip.run_cell('a = """\n%exit\n"""') |
|
106 | ip.run_cell('a = """\n%exit\n"""') | |
107 | self.assertEqual(ip.user_ns['a'], '\n%exit\n') |
|
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 | def test_trailing_newline(self): |
|
109 | def test_trailing_newline(self): | |
121 | """test that running !(command) does not raise a SyntaxError""" |
|
110 | """test that running !(command) does not raise a SyntaxError""" | |
122 | ip.run_cell('!(true)\n', False) |
|
111 | ip.run_cell('!(true)\n', False) |
@@ -48,16 +48,16 b' class DummyMagics(magic.Magics): pass' | |||||
48 | def test_rehashx(): |
|
48 | def test_rehashx(): | |
49 | # clear up everything |
|
49 | # clear up everything | |
50 | _ip = get_ipython() |
|
50 | _ip = get_ipython() | |
51 |
_ip.alias_manager. |
|
51 | _ip.alias_manager.clear_aliases() | |
52 | del _ip.db['syscmdlist'] |
|
52 | del _ip.db['syscmdlist'] | |
53 |
|
53 | |||
54 | _ip.magic('rehashx') |
|
54 | _ip.magic('rehashx') | |
55 | # Practically ALL ipython development systems will have more than 10 aliases |
|
55 | # Practically ALL ipython development systems will have more than 10 aliases | |
56 |
|
56 | |||
57 |
nt.assert_true(len(_ip.alias_manager.alias |
|
57 | nt.assert_true(len(_ip.alias_manager.aliases) > 10) | |
58 |
for |
|
58 | for name, cmd in _ip.alias_manager.aliases: | |
59 | # we must strip dots from alias names |
|
59 | # we must strip dots from alias names | |
60 |
nt.assert_not_in('.', |
|
60 | nt.assert_not_in('.', name) | |
61 |
|
61 | |||
62 | # rehashx must fill up syscmdlist |
|
62 | # rehashx must fill up syscmdlist | |
63 | scoms = _ip.db['syscmdlist'] |
|
63 | scoms = _ip.db['syscmdlist'] |
@@ -210,17 +210,17 b' class StoreMagics(Magics, Configurable):' | |||||
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 | name = args[0] | |
214 | if args[0] in ip.alias_manager: |
|
214 | try: | |
215 | name = args[0] |
|
215 | cmd = ip.alias_manager.retrieve_alias(name) | |
216 | nargs, cmd = ip.alias_manager.alias_table[ name ] |
|
216 | except ValueError: | |
217 | staliases = db.get('stored_aliases',{}) |
|
217 | raise UsageError("Unknown variable '%s'" % name) | |
218 | staliases[ name ] = cmd |
|
218 | ||
219 |
|
|
219 | staliases = db.get('stored_aliases',{}) | |
220 |
|
|
220 | staliases[name] = cmd | |
221 | return |
|
221 | db['stored_aliases'] = staliases | |
222 | else: |
|
222 | print "Alias stored: %s (%s)" % (name, cmd) | |
223 | raise UsageError("Unknown variable '%s'" % args[0]) |
|
223 | return | |
224 |
|
224 | |||
225 | else: |
|
225 | else: | |
226 | modname = getattr(inspect.getmodule(obj), '__name__', '') |
|
226 | modname = getattr(inspect.getmodule(obj), '__name__', '') |
@@ -27,7 +27,7 b' def test_store_restore():' | |||||
27 | # Check restoring |
|
27 | # Check restoring | |
28 | ip.magic('store -r') |
|
28 | ip.magic('store -r') | |
29 | nt.assert_equal(ip.user_ns['foo'], 78) |
|
29 | nt.assert_equal(ip.user_ns['foo'], 78) | |
30 |
|
|
30 | assert ip.alias_manager.is_alias('bar') | |
31 | nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh']) |
|
31 | nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh']) | |
32 |
|
32 | |||
33 | os.rmdir(tmpd) |
|
33 | os.rmdir(tmpd) |
@@ -369,7 +369,7 b' class TerminalInteractiveShell(InteractiveShell):' | |||||
369 |
|
369 | |||
370 |
|
370 | |||
371 | for name, cmd in aliases: |
|
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 | # Things related to the banner and usage |
|
375 | # Things related to the banner and usage |
General Comments 0
You need to be logged in to leave comments.
Login now