##// END OF EJS Templates
Applied Olivier Lauzannes patch for cleaner import completer
vivainio -
Show More
@@ -1,226 +1,297 b''
1 1 #!/usr/bin/env python
2 2
3 3 """ Implementations for various useful completers
4 4
5 5 See Extensions/ipy_stock_completers.py on examples of how to enable a completer,
6 6 but the basic idea is to do:
7 7
8 8 ip.set_hook('complete_command', svn_completer, str_key = 'svn')
9 9
10 10 """
11 11
12 12 import IPython.ipapi
13 13 import glob,os,shlex,sys
14
14 import inspect
15 15 ip = IPython.ipapi.get()
16 16
17 def getRootModules():
18 """
19 Returns a list containing the names of all the modules available in the
20 folders of the pythonpath.
21 """
22 modules = []
23 for path in sys.path:
24 modules += moduleList(path)
25 modules += sys.builtin_module_names
26 modules = list(set(modules))
27 if '__init__' in modules:
28 modules.remove('__init__')
29 return list(set(modules))
30
31 def moduleList(path):
32 """
33 Return the list containing the names of the modules available in the given
34 folder.
35 """
36 folder_list = glob.glob(os.path.join(path,'*'))
37 folder_list = [path for path in folder_list \
38 if (os.path.isdir(path) and os.path.exists(os.path.join(path,'__init__.py')))\
39 or path[-3:] in ('.py','.so')\
40 or path[-4:] in ('.pyc','.pyo')]
41 folder_list += folder_list
42 folder_list = [os.path.basename(path).split('.')[0] for path in folder_list]
43 return folder_list
44
45 def moduleCompletion(line):
46 """
47 Returns a list containing the completion possibilities for an import line.
48 The line looks like this :
49 'import xml.d'
50 'from xml.dom import'
51 """
52 def tryImport(mod, only_modules=False):
53 def isImportable(module, attr):
54 if only_modules:
55 return inspect.ismodule(getattr(module, attr))
56 else:
57 return not(attr[:2] == '__' and attr[-2:] == '__')
58 try:
59 m = __import__(mod)
60 except:
61 return []
62 mods = mod.split('.')
63 for module in mods[1:]:
64 m = getattr(m,module)
65 if (not hasattr(m, '__file__')) or (not only_modules) or\
66 (hasattr(m, '__file__') and '__init__' in m.__file__):
67 completion_list = [attr for attr in dir(m) if isImportable(m, attr)]
68 completion_list.extend(getattr(m,'__all__',[]))
69 if hasattr(m, '__file__') and '__init__' in m.__file__:
70 completion_list.extend(moduleList(os.path.dirname(m.__file__)))
71 completion_list = list(set(completion_list))
72 if '__init__' in completion_list:
73 completion_list.remove('__init__')
74 return completion_list
75
76 words = line.split(' ')
77 if len(words) == 3 and words[0] == 'from':
78 return ['import ']
79 if len(words) < 3 and (words[0] in ['import','from']) :
80 if len(words) == 1:
81 return getRootModules()
82 mod = words[1].split('.')
83 if len(mod) < 2:
84 return getRootModules()
85 completion_list = tryImport('.'.join(mod[:-1]), True)
86 completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
87 return completion_list
88 if len(words) >= 3 and words[0] == 'from':
89 mod = words[1]
90 return tryImport(mod)
91
17 92 def vcs_completer(commands, event):
18 93 """ utility to make writing typical version control app completers easier
19 94
20 95 VCS command line apps typically have the format:
21 96
22 97 [sudo ]PROGNAME [help] [command] file file...
23 98
24 99 """
25 100
26 101
27 102 cmd_param = event.line.split()
28 103 if event.line.endswith(' '):
29 104 cmd_param.append('')
30 105
31 106 if cmd_param[0] == 'sudo':
32 107 cmd_param = cmd_param[1:]
33 108
34 109 if len(cmd_param) == 2 or 'help' in cmd_param:
35 110 return commands.split()
36 111
37 112 return ip.IP.Completer.file_matches(event.symbol)
38 113
39 114
40 115
41 116 def apt_completers(self, event):
42 117 """ This should return a list of strings with possible completions.
43 118
44 119 Note that all the included strings that don't start with event.symbol
45 120 are removed, in order to not confuse readline.
46 121
47 122 """
48 123 # print event # dbg
49 124
50 125 # commands are only suggested for the 'command' part of package manager
51 126 # invocation
52 127
53 128 cmd = (event.line + "<placeholder>").rsplit(None,1)[0]
54 129 # print cmd
55 130 if cmd.endswith('apt-get') or cmd.endswith('yum'):
56 131 return ['update', 'upgrade', 'install', 'remove']
57 132
58 133 # later on, add dpkg -l / whatever to get list of possible
59 134 # packages, add switches etc. for the rest of command line
60 135 # filling
61 136
62 137 raise IPython.ipapi.TryNext
63 138
64 139
65 140
66 141 pkg_cache = None
67 142
68 143 def module_completer(self,event):
69 """ Give completions after user has typed 'import'.
70
71 Note that only possible completions in the local directory are returned."""
144 """ Give completions after user has typed 'import ...' or 'from ...'"""
72 145
73 146 # This works in all versions of python. While 2.5 has
74 147 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
75 148 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
76 # of possibly problematic side effects. At some point we may implement
77 # something that searches sys.path in a saner/safer way, but for now we'll
78 # restrict ourselves to local completions only.
79 for el in [f[:-3] for f in glob.glob("*.py")]:
80 yield el
81 return
149 # of possibly problematic side effects.
150 # This search the folders in the sys.path for available modules.
151
152 return moduleCompletion(event.line)
82 153
83 154
84 155 svn_commands = """\
85 156 add blame praise annotate ann cat checkout co cleanup commit ci copy
86 157 cp delete del remove rm diff di export help ? h import info list ls
87 158 lock log merge mkdir move mv rename ren propdel pdel pd propedit pedit
88 159 pe propget pget pg proplist plist pl propset pset ps resolved revert
89 160 status stat st switch sw unlock update
90 161 """
91 162
92 163 def svn_completer(self,event):
93 164 return vcs_completer(svn_commands, event)
94 165
95 166
96 167 hg_commands = """
97 168 add addremove annotate archive backout branch branches bundle cat
98 169 clone commit copy diff export grep heads help identify import incoming
99 170 init locate log manifest merge outgoing parents paths pull push
100 171 qapplied qclone qcommit qdelete qdiff qfold qguard qheader qimport
101 172 qinit qnew qnext qpop qprev qpush qrefresh qrename qrestore qsave
102 173 qselect qseries qtop qunapplied recover remove rename revert rollback
103 174 root serve showconfig status strip tag tags tip unbundle update verify
104 175 version
105 176 """
106 177
107 178 def hg_completer(self,event):
108 179 """ Completer for mercurial commands """
109 180
110 181 return vcs_completer(hg_commands, event)
111 182
112 183
113 184
114 185 bzr_commands = """
115 186 add annotate bind branch break-lock bundle-revisions cat check
116 187 checkout commit conflicts deleted diff export gannotate gbranch
117 188 gcommit gdiff help ignore ignored info init init-repository inventory
118 189 log merge missing mkdir mv nick pull push reconcile register-branch
119 190 remerge remove renames resolve revert revno root serve sign-my-commits
120 191 status testament unbind uncommit unknowns update upgrade version
121 192 version-info visualise whoami
122 193 """
123 194
124 195 def bzr_completer(self,event):
125 196 """ Completer for bazaar commands """
126 197 cmd_param = event.line.split()
127 198 if event.line.endswith(' '):
128 199 cmd_param.append('')
129 200
130 201 if len(cmd_param) > 2:
131 202 cmd = cmd_param[1]
132 203 param = cmd_param[-1]
133 204 output_file = (param == '--output=')
134 205 if cmd == 'help':
135 206 return bzr_commands.split()
136 207 elif cmd in ['bundle-revisions','conflicts',
137 208 'deleted','nick','register-branch',
138 209 'serve','unbind','upgrade','version',
139 210 'whoami'] and not output_file:
140 211 return []
141 212 else:
142 213 # the rest are probably file names
143 214 return ip.IP.Completer.file_matches(event.symbol)
144 215
145 216 return bzr_commands.split()
146 217
147 218
148 219 def shlex_split(x):
149 220 """Helper function to split lines into segments."""
150 221 #shlex.split raise exception if syntax error in sh syntax
151 222 #for example if no closing " is found. This function keeps dropping
152 223 #the last character of the line until shlex.split does not raise
153 224 #exception. Adds end of the line to the result of shlex.split
154 225 #example: %run "c:/python -> ['%run','"c:/python']
155 226 endofline=[]
156 227 while x!="":
157 228 try:
158 229 comps=shlex.split(x)
159 230 if len(endofline)>=1:
160 231 comps.append("".join(endofline))
161 232 return comps
162 233 except ValueError:
163 234 endofline=[x[-1:]]+endofline
164 235 x=x[:-1]
165 236 return ["".join(endofline)]
166 237
167 238 def runlistpy(self, event):
168 239 comps = shlex_split(event.line)
169 240 relpath = (len(comps) > 1 and comps[-1] or '').strip("'\"")
170 241
171 242 #print "\nev=",event # dbg
172 243 #print "rp=",relpath # dbg
173 244 #print 'comps=',comps # dbg
174 245
175 246 lglob = glob.glob
176 247 isdir = os.path.isdir
177 248 if relpath.startswith('~'):
178 249 relpath = os.path.expanduser(relpath)
179 250 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*')
180 251 if isdir(f)]
181 252
182 253 # Find if the user has already typed the first filename, after which we
183 254 # should complete on all files, since after the first one other files may
184 255 # be arguments to the input script.
185 256 #filter(
186 257 if filter(lambda f: f.endswith('.py') or f.endswith('.ipy'),comps):
187 258 pys = [f.replace('\\','/') for f in lglob('*')]
188 259 else:
189 260 pys = [f.replace('\\','/')
190 261 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy')]
191 262 return dirs + pys
192 263
193 264
194 265 def cd_completer(self, event):
195 266 relpath = event.symbol
196 267 #print event # dbg
197 268 if '-b' in event.line:
198 269 # return only bookmark completions
199 270 bkms = self.db.get('bookmarks',{})
200 271 return bkms.keys()
201 272
202 273
203 274 if event.symbol == '-':
204 275 # jump in directory history by number
205 276 ents = ['-%d [%s]' % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
206 277 if len(ents) > 1:
207 278 return ents
208 279 return []
209 280
210 281 if relpath.startswith('~'):
211 282 relpath = os.path.expanduser(relpath).replace('\\','/')
212 283 found = []
213 284 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
214 285 if os.path.isdir(f)]:
215 286 if ' ' in d:
216 287 # we don't want to deal with any of that, complex code
217 288 # for this is elsewhere
218 289 raise IPython.ipapi.TryNext
219 290 found.append( d )
220 291
221 292 if not found:
222 293 if os.path.isdir(relpath):
223 294 return [relpath]
224 295 raise IPython.ipapi.TryNext
225 296 return found
226 297
General Comments 0
You need to be logged in to leave comments. Login now