~ubuntu-branches/debian/experimental/spyder/experimental

« back to all changes in this revision

Viewing changes to spyderlib/utils/module_completion.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2014-05-29 09:06:26 UTC
  • mfrom: (1.1.21) (18.1.6 sid)
  • Revision ID: package-import@ubuntu.com-20140529090626-f58t82g0n5iewaxu
Tags: 2.3.0~rc+dfsg-1~experimental2
* Add spyder-common binary package for all the python2,3 common files
* debian/path
  - 0001-fix-documentation-installation.patch (deleted)
  + 0001-fix-spyderlib-path.patch (new)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
"""Module completion auxiliary functions"""
3
 
 
4
 
#------------------------------------------------------------------------------
5
 
#
6
 
#  Most functions on this file were taken from the file core/completerlib,
7
 
#  which belongs to the IPython project (v0.13). They were added here because
8
 
#  a) IPython is not an Spyder runtime dependency, and b) we want to perfom
9
 
#  module completion not only on our Python console, but also on our source
10
 
#  code editor.
11
 
#
12
 
#  Several of these functions were modified to make it work according to our
13
 
#  needs
14
 
#
15
 
#  Distributed under the terms of the BSD License.
16
 
#  Copyright (C) 2010-2011 The IPython Development Team.
17
 
#  Copyright (C) 2013 The Spyder Development Team
18
 
#
19
 
#------------------------------------------------------------------------------
20
 
 
21
 
import imp
22
 
import inspect
23
 
import os.path
24
 
import pkgutil
25
 
import re
26
 
from time import time
27
 
import sys
28
 
from zipimport import zipimporter
29
 
 
30
 
from spyderlib.baseconfig import get_conf_path
31
 
from spyderlib.utils.external.pickleshare import PickleShareDB
32
 
 
33
 
#-----------------------------------------------------------------------------
34
 
# Globals and constants
35
 
#-----------------------------------------------------------------------------
36
 
 
37
 
# Path to the modules database
38
 
MODULES_PATH = get_conf_path('db')
39
 
 
40
 
# Time in seconds after which we give up
41
 
TIMEOUT_GIVEUP = 20
42
 
 
43
 
# Py2app only uses .pyc files for the stdlib when optimize=0,
44
 
# so we need to add it as another suffix here
45
 
if sys.platform == 'darwin' and 'Spyder.app' in __file__:
46
 
    suffixes = imp.get_suffixes() + [('.pyc', 'rb', '2')]
47
 
else:
48
 
    suffixes = imp.get_suffixes()
49
 
 
50
 
# Regular expression for the python import statement
51
 
import_re = re.compile(r'(?P<name>[a-zA-Z_][a-zA-Z0-9_]*?)'
52
 
                       r'(?P<package>[/\\]__init__)?'
53
 
                       r'(?P<suffix>%s)$' %
54
 
                       r'|'.join(re.escape(s[0]) for s in suffixes))
55
 
 
56
 
# Modules database
57
 
modules_db = PickleShareDB(MODULES_PATH)
58
 
 
59
 
#-----------------------------------------------------------------------------
60
 
# Utility functions
61
 
#-----------------------------------------------------------------------------
62
 
 
63
 
def module_list(path):
64
 
    """
65
 
    Return the list containing the names of the modules available in the given
66
 
    folder.
67
 
    """
68
 
    # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
69
 
    if path == '':
70
 
        path = '.'
71
 
 
72
 
    # A few local constants to be used in loops below
73
 
    pjoin = os.path.join
74
 
 
75
 
    if os.path.isdir(path):
76
 
        # Build a list of all files in the directory and all files
77
 
        # in its subdirectories. For performance reasons, do not
78
 
        # recurse more than one level into subdirectories.
79
 
        files = []
80
 
        for root, dirs, nondirs in os.walk(path):
81
 
            subdir = root[len(path)+1:]
82
 
            if subdir:
83
 
                files.extend(pjoin(subdir, f) for f in nondirs)
84
 
                dirs[:] = [] # Do not recurse into additional subdirectories.
85
 
            else:
86
 
                files.extend(nondirs)
87
 
    else:
88
 
        try:
89
 
            files = list(zipimporter(path)._files.keys())
90
 
        except:
91
 
            files = []
92
 
 
93
 
    # Build a list of modules which match the import_re regex.
94
 
    modules = []
95
 
    for f in files:
96
 
        m = import_re.match(f)
97
 
        if m:
98
 
            modules.append(m.group('name'))
99
 
    return list(set(modules))
100
 
 
101
 
 
102
 
def get_root_modules(paths):
103
 
    """
104
 
    Returns list of names of all modules from PYTHONPATH folders.
105
 
    
106
 
    paths : list
107
 
        A list of additional paths that Spyder adds to PYTHONPATH. They are
108
 
        comming from our PYTHONPATH manager and from the currently selected
109
 
        project.
110
 
    """
111
 
    modules = []
112
 
    spy_modules = []
113
 
    
114
 
    for path in paths:
115
 
        spy_modules += module_list(path)
116
 
    spy_modules = set(spy_modules)
117
 
    if '__init__' in spy_modules:
118
 
        spy_modules.remove('__init__')
119
 
    spy_modules = list(spy_modules)
120
 
    
121
 
    if modules_db.has_key('rootmodules'):
122
 
        return spy_modules + modules_db['rootmodules']
123
 
 
124
 
    t = time()
125
 
    modules = list(sys.builtin_module_names)
126
 
    # TODO: Change this sys.path for console's interpreter sys.path
127
 
    for path in sys.path:
128
 
        modules += module_list(path)        
129
 
        if time() - t > TIMEOUT_GIVEUP:
130
 
            print "Module list generation is taking too long, we give up.\n"
131
 
            modules_db['rootmodules'] = []
132
 
            return []
133
 
    
134
 
    modules = set(modules)
135
 
    excluded_modules = ['__init__'] + spy_modules
136
 
    for mod in excluded_modules:
137
 
        if mod in modules:
138
 
            modules.remove(mod)
139
 
    modules = list(modules)
140
 
 
141
 
    modules_db['rootmodules'] = modules
142
 
    return spy_modules + modules
143
 
 
144
 
 
145
 
def get_submodules(mod):
146
 
    """Get all submodules of a given module"""
147
 
    def catch_exceptions(module):
148
 
        pass
149
 
    try:
150
 
        m = __import__(mod)
151
 
        submodules = [mod]
152
 
        submods = pkgutil.walk_packages(m.__path__, m.__name__ + '.',
153
 
                                        catch_exceptions)
154
 
        for sm in submods:
155
 
            sm_name = sm[1]
156
 
            submodules.append(sm_name)
157
 
    except ImportError:
158
 
        return []
159
 
    except:
160
 
        return [mod]
161
 
    
162
 
    return submodules
163
 
 
164
 
 
165
 
def is_importable(module, attr, only_modules):
166
 
    if only_modules:
167
 
        return inspect.ismodule(getattr(module, attr))
168
 
    else:
169
 
        return not(attr[:2] == '__' and attr[-2:] == '__')
170
 
 
171
 
 
172
 
def try_import(mod, only_modules=False):
173
 
    try:
174
 
        m = __import__(mod)
175
 
    except:
176
 
        return []
177
 
    mods = mod.split('.')
178
 
    for module in mods[1:]:
179
 
        m = getattr(m, module)
180
 
 
181
 
    m_is_init = hasattr(m, '__file__') and '__init__' in m.__file__
182
 
 
183
 
    completions = []
184
 
    if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
185
 
        completions.extend([attr for attr in dir(m) if
186
 
                            is_importable(m, attr, only_modules)])
187
 
 
188
 
    completions.extend(getattr(m, '__all__', []))
189
 
    if m_is_init:
190
 
        completions.extend(module_list(os.path.dirname(m.__file__)))
191
 
    completions = set(completions)
192
 
    if '__init__' in completions:
193
 
        completions.remove('__init__')
194
 
    return list(completions)
195
 
 
196
 
 
197
 
def dot_completion(mod, paths):
198
 
    if len(mod) < 2:
199
 
        return filter(lambda x: x.startswith(mod[0]), get_root_modules(paths))
200
 
    completion_list = try_import('.'.join(mod[:-1]), True)
201
 
    completion_list = filter(lambda x: x.startswith(mod[-1]), completion_list)
202
 
    completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
203
 
    return completion_list
204
 
 
205
 
#-----------------------------------------------------------------------------
206
 
# Main functions
207
 
#-----------------------------------------------------------------------------
208
 
 
209
 
def module_completion(line, paths=[]):
210
 
    """
211
 
    Returns a list containing the completion possibilities for an import line.
212
 
    
213
 
    The line looks like this :
214
 
    'import xml.d'
215
 
    'from xml.dom import'
216
 
    """
217
 
 
218
 
    words = line.split(' ')
219
 
    nwords = len(words)
220
 
    
221
 
    # from whatever <tab> -> 'import '
222
 
    if nwords == 3 and words[0] == 'from':
223
 
        if words[2].startswith('i') or words[2] == '':
224
 
            return ['import ']
225
 
        else:
226
 
            return []
227
 
 
228
 
    # 'import xy<tab> or import xy<tab>, '
229
 
    if words[0] == 'import':
230
 
        if nwords == 2 and words[1] == '':
231
 
            return get_root_modules(paths)
232
 
        if ',' == words[-1][-1]:
233
 
            return [' ']       
234
 
        mod = words[-1].split('.')
235
 
        return dot_completion(mod, paths)
236
 
 
237
 
    # 'from xy<tab>'
238
 
    if nwords < 3 and (words[0] == 'from'):
239
 
        if nwords == 1:
240
 
            return get_root_modules(paths)
241
 
        mod = words[1].split('.')
242
 
        return dot_completion(mod, paths)
243
 
 
244
 
    # 'from xyz import abc<tab>'
245
 
    if nwords >= 3 and words[0] == 'from':
246
 
        mod = words[1]
247
 
        completion_list = try_import(mod)
248
 
        if words[2] == 'import' and words[3] != '':
249
 
            if '(' in words[-1]:
250
 
                words = words[:-2] + words[-1].split('(')
251
 
            if ',' in words[-1]:
252
 
                words = words[:-2] + words[-1].split(',')
253
 
            return filter(lambda x: x.startswith(words[-1]), completion_list)
254
 
        else:
255
 
            return completion_list
256
 
    
257
 
    return []
258
 
        
259
 
 
260
 
def reset():
261
 
    """Clear root modules database"""
262
 
    if modules_db.has_key('rootmodules'):
263
 
        del modules_db['rootmodules']
264
 
 
265
 
 
266
 
def get_preferred_submodules():
267
 
    """
268
 
    Get all submodules of the main scientific modules and others of our
269
 
    interest
270
 
    """
271
 
    if modules_db.has_key('submodules'):
272
 
        return modules_db['submodules']
273
 
    
274
 
    mods = ['numpy', 'scipy', 'sympy', 'pandas', 'networkx', 'statsmodels',
275
 
            'matplotlib', 'sklearn', 'skimage', 'mpmath', 'os', 'PIL',
276
 
            'OpenGL', 'array', 'audioop', 'binascii', 'cPickle', 'cStringIO',
277
 
            'cmath', 'collections', 'datetime', 'errno', 'exceptions', 'gc',
278
 
            'imageop', 'imp', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt',
279
 
            'nt', 'operator', 'parser', 'rgbimg', 'signal', 'strop', 'sys',
280
 
            'thread', 'time', 'wx', 'xxsubtype', 'zipimport', 'zlib', 'nose',
281
 
            'PyQt4', 'PySide', 'os.path']
282
 
 
283
 
    submodules = []
284
 
    for m in mods:
285
 
        submods = get_submodules(m)
286
 
        submodules += submods
287
 
    
288
 
    modules_db['submodules'] = submodules
289
 
    return submodules
290
 
 
291
 
#-----------------------------------------------------------------------------
292
 
# Tests
293
 
#-----------------------------------------------------------------------------
294
 
 
295
 
if __name__ == "__main__":
296
 
    # Some simple tests.
297
 
    # Sort operations are done by the completion widget, so we have to
298
 
    # replicate them here.
299
 
    # We've chosen to use xml on most tests because it's on the standard
300
 
    # library. This way we can ensure they work on all plataforms.
301
 
    
302
 
    assert sorted(module_completion('import xml.')) == \
303
 
        ['xml.dom', 'xml.etree', 'xml.parsers', 'xml.sax']
304
 
 
305
 
    assert sorted(module_completion('import xml.d')) ==  ['xml.dom']
306
 
 
307
 
    assert module_completion('from xml.etree ') == ['import ']
308
 
 
309
 
    assert sorted(module_completion('from xml.etree import '), key=str.lower) ==\
310
 
        ['cElementTree', 'ElementInclude', 'ElementPath', 'ElementTree']
311
 
 
312
 
    assert module_completion('import sys, zl') == ['zlib']
313
 
 
314
 
    s = 'from xml.etree.ElementTree import '
315
 
    assert module_completion(s + 'V') == ['VERSION']
316
 
 
317
 
    assert sorted(module_completion(s + 'VERSION, XM')) == \
318
 
        ['XML', 'XMLID', 'XMLParser', 'XMLTreeBuilder']
319
 
 
320
 
    assert module_completion(s + '(dum') == ['dump']
321
 
 
322
 
    assert module_completion(s + '(dump, Su') == ['SubElement']