1import inspect 2import os.path 3import sys 4 5from _pydev_bundle._pydev_tipper_common import do_find 6from _pydevd_bundle.pydevd_constants import IS_PY2 7from _pydevd_bundle.pydevd_resolver import suppress_warnings 8 9if IS_PY2: 10 from inspect import getargspec as _originalgetargspec 11 def getargspec(*args, **kwargs): 12 ret = list(_originalgetargspec(*args, **kwargs)) 13 ret.append([]) 14 ret.append({}) 15 return ret 16 17else: 18 from inspect import getfullargspec 19 20 def getargspec(*args, **kwargs): 21 arg_spec = getfullargspec(*args, **kwargs) 22 return arg_spec.args, arg_spec.varargs, arg_spec.varkw, arg_spec.defaults, arg_spec.kwonlyargs or [], arg_spec.kwonlydefaults or {} 23 24try: 25 xrange 26except: 27 xrange = range 28 29#completion types. 30TYPE_IMPORT = '0' 31TYPE_CLASS = '1' 32TYPE_FUNCTION = '2' 33TYPE_ATTR = '3' 34TYPE_BUILTIN = '4' 35TYPE_PARAM = '5' 36 37# completion types for IPython console 38TYPE_IPYTHON = '11' 39TYPE_IPYTHON_MAGIC = '12' 40 41def _imp(name, log=None): 42 try: 43 return __import__(name) 44 except: 45 if '.' in name: 46 sub = name[0:name.rfind('.')] 47 48 if log is not None: 49 log.add_content('Unable to import', name, 'trying with', sub) 50 log.add_exception() 51 52 return _imp(sub, log) 53 else: 54 s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path) 55 if log is not None: 56 log.add_content(s) 57 log.add_exception() 58 59 raise ImportError(s) 60 61 62IS_IPY = False 63if sys.platform == 'cli': 64 IS_IPY = True 65 _old_imp = _imp 66 def _imp(name, log=None): 67 #We must add a reference in clr for .Net 68 import clr #@UnresolvedImport 69 initial_name = name 70 while '.' in name: 71 try: 72 clr.AddReference(name) 73 break #If it worked, that's OK. 74 except: 75 name = name[0:name.rfind('.')] 76 else: 77 try: 78 clr.AddReference(name) 79 except: 80 pass #That's OK (not dot net module). 81 82 return _old_imp(initial_name, log) 83 84 85 86def get_file(mod): 87 f = None 88 try: 89 f = inspect.getsourcefile(mod) or inspect.getfile(mod) 90 except: 91 if hasattr(mod, '__file__'): 92 f = mod.__file__ 93 if f.lower(f[-4:]) in ['.pyc', '.pyo']: 94 filename = f[:-4] + '.py' 95 if os.path.exists(filename): 96 f = filename 97 98 return f 99 100def Find(name, log=None): 101 f = None 102 103 mod = _imp(name, log) 104 parent = mod 105 foundAs = '' 106 107 if inspect.ismodule(mod): 108 f = get_file(mod) 109 110 components = name.split('.') 111 112 old_comp = None 113 for comp in components[1:]: 114 try: 115 #this happens in the following case: 116 #we have mx.DateTime.mxDateTime.mxDateTime.pyd 117 #but after importing it, mx.DateTime.mxDateTime shadows access to mxDateTime.pyd 118 mod = getattr(mod, comp) 119 except AttributeError: 120 if old_comp != comp: 121 raise 122 123 if inspect.ismodule(mod): 124 f = get_file(mod) 125 else: 126 if len(foundAs) > 0: 127 foundAs = foundAs + '.' 128 foundAs = foundAs + comp 129 130 old_comp = comp 131 132 return f, mod, parent, foundAs 133 134def search_definition(data): 135 '''@return file, line, col 136 ''' 137 138 data = data.replace('\n', '') 139 if data.endswith('.'): 140 data = data.rstrip('.') 141 f, mod, parent, foundAs = Find(data) 142 try: 143 return do_find(f, mod), foundAs 144 except: 145 return do_find(f, parent), foundAs 146 147 148def generate_tip(data, log=None): 149 data = data.replace('\n', '') 150 if data.endswith('.'): 151 data = data.rstrip('.') 152 153 f, mod, parent, foundAs = Find(data, log) 154 #print_ >> open('temp.txt', 'w'), f 155 tips = generate_imports_tip_for_module(mod) 156 return f, tips 157 158 159def check_char(c): 160 if c == '-' or c == '.': 161 return '_' 162 return c 163 164_SENTINEL = object() 165 166def generate_imports_tip_for_module(obj_to_complete, dir_comps=None, getattr=getattr, filter=lambda name:True): 167 ''' 168 @param obj_to_complete: the object from where we should get the completions 169 @param dir_comps: if passed, we should not 'dir' the object and should just iterate those passed as kwonly_arg parameter 170 @param getattr: the way to get kwonly_arg given object from the obj_to_complete (used for the completer) 171 @param filter: kwonly_arg callable that receives the name and decides if it should be appended or not to the results 172 @return: list of tuples, so that each tuple represents kwonly_arg completion with: 173 name, doc, args, type (from the TYPE_* constants) 174 ''' 175 ret = [] 176 177 if dir_comps is None: 178 dir_comps = dir(obj_to_complete) 179 if hasattr(obj_to_complete, '__dict__'): 180 dir_comps.append('__dict__') 181 if hasattr(obj_to_complete, '__class__'): 182 dir_comps.append('__class__') 183 # Fix for PY-38151 - From python doc, metaclass attributes are not in the list returned by `dir`. 184 # This is why e.g. __name__ does not appear. Let's add the metaclass attributes here. 185 try: 186 if inspect.isclass(obj_to_complete): 187 dir_comps += dir(type(obj_to_complete)) 188 # do not do it from instance, it might grab irrelevant items 189 except: 190 # ignore any error just in case 191 pass 192 193 get_complete_info = True 194 195 if len(dir_comps) > 1000: 196 #ok, we don't want to let our users wait forever... 197 #no complete info for you... 198 199 get_complete_info = False 200 201 dontGetDocsOn = (float, int, str, tuple, list) 202 for d in dir_comps: 203 204 if d is None: 205 continue 206 207 if not filter(d): 208 continue 209 210 args = '' 211 212 try: 213 with suppress_warnings(): 214 try: 215 obj = getattr(obj_to_complete.__class__, d) 216 except: 217 obj = getattr(obj_to_complete, d) 218 except: #just ignore and get it without additional info 219 ret.append((d, '', args, TYPE_BUILTIN)) 220 else: 221 222 if get_complete_info: 223 try: 224 retType = TYPE_BUILTIN 225 226 #check if we have to get docs 227 getDoc = True 228 for class_ in dontGetDocsOn: 229 230 if isinstance(obj, class_): 231 getDoc = False 232 break 233 234 doc = '' 235 if getDoc: 236 #no need to get this info... too many constants are defined and 237 #makes things much slower (passing all that through sockets takes quite some time) 238 try: 239 doc = inspect.getdoc(obj) 240 if doc is None: 241 doc = '' 242 except: #may happen on jython when checking java classes (so, just ignore it) 243 doc = '' 244 245 if inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj) \ 246 or inspect.isgetsetdescriptor(obj) or inspect.ismemberdescriptor(obj): 247 # Fix for PY-38151: `obj` is a descriptor definition, not a called descriptor 248 # (`obj_to_complete` is the class defining it). 249 retType = TYPE_ATTR 250 251 elif inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj): 252 try: 253 args, vargs, kwargs, defaults, kwonly_args, kwonly_defaults = getargspec(obj) 254 255 args = args[:] 256 257 for kwonly_arg in kwonly_args: 258 default = kwonly_defaults.get(kwonly_arg, _SENTINEL) 259 if default is not _SENTINEL: 260 args.append('%s=%s' % (kwonly_arg, default)) 261 else: 262 args.append(str(kwonly_arg)) 263 264 args = '(%s)' % (', '.join(args)) 265 except TypeError: 266 #ok, let's see if we can get the arguments from the doc 267 args, doc = signature_from_docstring(doc, getattr(obj, '__name__', None)) 268 269 retType = TYPE_FUNCTION 270 271 elif inspect.isclass(obj): 272 retType = TYPE_CLASS 273 274 elif inspect.ismodule(obj): 275 retType = TYPE_IMPORT 276 277 else: 278 retType = TYPE_ATTR 279 280 281 #add token and doc to return - assure only strings. 282 ret.append((d, doc, args, retType)) 283 284 except: #just ignore and get it without aditional info 285 ret.append((d, '', args, TYPE_BUILTIN)) 286 287 else: #get_complete_info == False 288 if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj): 289 retType = TYPE_FUNCTION 290 291 elif inspect.isclass(obj): 292 retType = TYPE_CLASS 293 294 elif inspect.ismodule(obj): 295 retType = TYPE_IMPORT 296 297 else: 298 retType = TYPE_ATTR 299 #ok, no complete info, let's try to do this as fast and clean as possible 300 #so, no docs for this kind of information, only the signatures 301 ret.append((d, '', str(args), retType)) 302 303 return ret 304 305 306def signature_from_docstring(doc, obj_name): 307 args = '()' 308 try: 309 found = False 310 if len(doc) > 0: 311 if IS_IPY: 312 # Handle case where we have the situation below 313 # sort(self, object cmp, object key) 314 # sort(self, object cmp, object key, bool reverse) 315 # sort(self) 316 # sort(self, object cmp) 317 318 # Or: sort(self: list, cmp: object, key: object) 319 # sort(self: list, cmp: object, key: object, reverse: bool) 320 # sort(self: list) 321 # sort(self: list, cmp: object) 322 if obj_name: 323 name = obj_name + '(' 324 325 # Fix issue where it was appearing sort(aa)sort(bb)sort(cc) in the same line. 326 lines = doc.splitlines() 327 if len(lines) == 1: 328 c = doc.count(name) 329 if c > 1: 330 doc = ('\n' + name).join(doc.split(name)) 331 332 major = '' 333 for line in doc.splitlines(): 334 if line.startswith(name) and line.endswith(')'): 335 if len(line) > len(major): 336 major = line 337 if major: 338 args = major[major.index('('):] 339 found = True 340 341 if not found: 342 i = doc.find('->') 343 if i < 0: 344 i = doc.find('--') 345 if i < 0: 346 i = doc.find('\n') 347 if i < 0: 348 i = doc.find('\r') 349 350 if i > 0: 351 s = doc[0:i] 352 s = s.strip() 353 354 # let's see if we have a docstring in the first line 355 if s[-1] == ')': 356 start = s.find('(') 357 if start >= 0: 358 end = s.find('[') 359 if end <= 0: 360 end = s.find(')') 361 if end <= 0: 362 end = len(s) 363 364 args = s[start:end] 365 if not args[-1] == ')': 366 args = args + ')' 367 368 # now, get rid of unwanted chars 369 l = len(args) - 1 370 r = [] 371 for i in xrange(len(args)): 372 if i == 0 or i == l: 373 r.append(args[i]) 374 else: 375 r.append(check_char(args[i])) 376 377 args = ''.join(r) 378 379 if IS_IPY: 380 if args.startswith('(self:'): 381 i = args.find(',') 382 if i >= 0: 383 args = '(self' + args[i:] 384 else: 385 args = '(self)' 386 i = args.find(')') 387 if i > 0: 388 args = args[:i + 1] 389 390 except: 391 pass 392 return args, doc 393