##// END OF EJS Templates
Merge pull request #13412 from bnavigator/backport-inspect...
Matthias Bussonnier -
r27352:7f253dcf merge
parent child Browse files
Show More
@@ -1,50 +1,51 b''
1 1 name: Run tests
2 2
3 3 on:
4 4 push:
5 5 pull_request:
6 6 # Run weekly on Monday at 1:23 UTC
7 7 schedule:
8 8 - cron: '23 1 * * 1'
9 9 workflow_dispatch:
10 10
11 11
12 12 jobs:
13 13 test:
14 14 runs-on: ${{ matrix.os }}
15 15 strategy:
16 16 matrix:
17 17 os: [ubuntu-latest]
18 python-version: ["3.7", "3.8", "3.9"]
18 python-version: ["3.7", "3.8", "3.9", "3.10"]
19 19 # Test all on ubuntu, test ends on macos
20 20 include:
21 21 - os: macos-latest
22 22 python-version: "3.7"
23 23 - os: macos-latest
24 24 python-version: "3.9"
25 25
26 26 steps:
27 27 - uses: actions/checkout@v2
28 28 - name: Set up Python ${{ matrix.python-version }}
29 29 uses: actions/setup-python@v2
30 30 with:
31 31 python-version: ${{ matrix.python-version }}
32 32 - name: Install and update Python dependencies
33 33 run: |
34 34 python -m pip install --upgrade pip setuptools wheel
35 35 python -m pip install --upgrade -e file://$PWD#egg=ipython[test]
36 36 python -m pip install --upgrade --upgrade-strategy eager trio curio
37 37 python -m pip install --upgrade pytest pytest-trio 'matplotlib!=3.2.0'
38 38 python -m pip install --upgrade check-manifest pytest-cov anyio
39 39 - name: Check manifest
40 40 run: check-manifest
41 41 - name: iptest
42 if: matrix.python-version != '3.10'
42 43 run: |
43 44 cd /tmp && iptest --coverage xml && cd -
44 45 cp /tmp/ipy_coverage.xml ./
45 46 cp /tmp/.coverage ./
46 47 - name: pytest
47 48 run: |
48 49 pytest
49 50 - name: Upload coverage to Codecov
50 51 uses: codecov/codecov-action@v2
@@ -1,1031 +1,1031 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Tools for inspecting Python objects.
3 3
4 4 Uses syntax highlighting for presenting the various information elements.
5 5
6 6 Similar in spirit to the inspect module, but all calls take a name argument to
7 7 reference the name under which an object is being read.
8 8 """
9 9
10 10 # Copyright (c) IPython Development Team.
11 11 # Distributed under the terms of the Modified BSD License.
12 12
13 13 __all__ = ['Inspector','InspectColors']
14 14
15 15 # stdlib modules
16 16 import ast
17 17 import inspect
18 18 from inspect import signature
19 19 import linecache
20 20 import warnings
21 21 import os
22 22 from textwrap import dedent
23 23 import types
24 24 import io as stdlib_io
25 25
26 26 from typing import Union
27 27
28 28 # IPython's own
29 29 from IPython.core import page
30 30 from IPython.lib.pretty import pretty
31 31 from IPython.testing.skipdoctest import skip_doctest
32 32 from IPython.utils import PyColorize
33 33 from IPython.utils import openpy
34 34 from IPython.utils import py3compat
35 35 from IPython.utils.dir2 import safe_hasattr
36 36 from IPython.utils.path import compress_user
37 37 from IPython.utils.text import indent
38 38 from IPython.utils.wildcard import list_namespace
39 39 from IPython.utils.wildcard import typestr2type
40 40 from IPython.utils.coloransi import TermColors, ColorScheme, ColorSchemeTable
41 41 from IPython.utils.py3compat import cast_unicode
42 42 from IPython.utils.colorable import Colorable
43 43 from IPython.utils.decorators import undoc
44 44
45 45 from pygments import highlight
46 46 from pygments.lexers import PythonLexer
47 47 from pygments.formatters import HtmlFormatter
48 48
49 49 def pylight(code):
50 50 return highlight(code, PythonLexer(), HtmlFormatter(noclasses=True))
51 51
52 52 # builtin docstrings to ignore
53 53 _func_call_docstring = types.FunctionType.__call__.__doc__
54 54 _object_init_docstring = object.__init__.__doc__
55 55 _builtin_type_docstrings = {
56 56 inspect.getdoc(t) for t in (types.ModuleType, types.MethodType,
57 57 types.FunctionType, property)
58 58 }
59 59
60 60 _builtin_func_type = type(all)
61 61 _builtin_meth_type = type(str.upper) # Bound methods have the same type as builtin functions
62 62 #****************************************************************************
63 63 # Builtin color schemes
64 64
65 65 Colors = TermColors # just a shorthand
66 66
67 67 InspectColors = PyColorize.ANSICodeColors
68 68
69 69 #****************************************************************************
70 70 # Auxiliary functions and objects
71 71
72 72 # See the messaging spec for the definition of all these fields. This list
73 73 # effectively defines the order of display
74 74 info_fields = ['type_name', 'base_class', 'string_form', 'namespace',
75 75 'length', 'file', 'definition', 'docstring', 'source',
76 76 'init_definition', 'class_docstring', 'init_docstring',
77 77 'call_def', 'call_docstring',
78 78 # These won't be printed but will be used to determine how to
79 79 # format the object
80 80 'ismagic', 'isalias', 'isclass', 'found', 'name'
81 81 ]
82 82
83 83
84 84 def object_info(**kw):
85 85 """Make an object info dict with all fields present."""
86 86 infodict = {k:None for k in info_fields}
87 87 infodict.update(kw)
88 88 return infodict
89 89
90 90
91 91 def get_encoding(obj):
92 92 """Get encoding for python source file defining obj
93 93
94 94 Returns None if obj is not defined in a sourcefile.
95 95 """
96 96 ofile = find_file(obj)
97 97 # run contents of file through pager starting at line where the object
98 98 # is defined, as long as the file isn't binary and is actually on the
99 99 # filesystem.
100 100 if ofile is None:
101 101 return None
102 102 elif ofile.endswith(('.so', '.dll', '.pyd')):
103 103 return None
104 104 elif not os.path.isfile(ofile):
105 105 return None
106 106 else:
107 107 # Print only text files, not extension binaries. Note that
108 108 # getsourcelines returns lineno with 1-offset and page() uses
109 109 # 0-offset, so we must adjust.
110 110 with stdlib_io.open(ofile, 'rb') as buffer: # Tweaked to use io.open for Python 2
111 111 encoding, lines = openpy.detect_encoding(buffer.readline)
112 112 return encoding
113 113
114 114 def getdoc(obj) -> Union[str,None]:
115 115 """Stable wrapper around inspect.getdoc.
116 116
117 117 This can't crash because of attribute problems.
118 118
119 119 It also attempts to call a getdoc() method on the given object. This
120 120 allows objects which provide their docstrings via non-standard mechanisms
121 121 (like Pyro proxies) to still be inspected by ipython's ? system.
122 122 """
123 123 # Allow objects to offer customized documentation via a getdoc method:
124 124 try:
125 125 ds = obj.getdoc()
126 126 except Exception:
127 127 pass
128 128 else:
129 129 if isinstance(ds, str):
130 130 return inspect.cleandoc(ds)
131 131 docstr = inspect.getdoc(obj)
132 132 return docstr
133 133
134 134
135 135 def getsource(obj, oname='') -> Union[str,None]:
136 136 """Wrapper around inspect.getsource.
137 137
138 138 This can be modified by other projects to provide customized source
139 139 extraction.
140 140
141 141 Parameters
142 142 ----------
143 143 obj : object
144 144 an object whose source code we will attempt to extract
145 145 oname : str
146 146 (optional) a name under which the object is known
147 147
148 148 Returns
149 149 -------
150 150 src : unicode or None
151 151
152 152 """
153 153
154 154 if isinstance(obj, property):
155 155 sources = []
156 156 for attrname in ['fget', 'fset', 'fdel']:
157 157 fn = getattr(obj, attrname)
158 158 if fn is not None:
159 159 encoding = get_encoding(fn)
160 160 oname_prefix = ('%s.' % oname) if oname else ''
161 161 sources.append(''.join(('# ', oname_prefix, attrname)))
162 162 if inspect.isfunction(fn):
163 163 sources.append(dedent(getsource(fn)))
164 164 else:
165 165 # Default str/repr only prints function name,
166 166 # pretty.pretty prints module name too.
167 167 sources.append(
168 168 '%s%s = %s\n' % (oname_prefix, attrname, pretty(fn))
169 169 )
170 170 if sources:
171 171 return '\n'.join(sources)
172 172 else:
173 173 return None
174 174
175 175 else:
176 176 # Get source for non-property objects.
177 177
178 178 obj = _get_wrapped(obj)
179 179
180 180 try:
181 181 src = inspect.getsource(obj)
182 182 except TypeError:
183 183 # The object itself provided no meaningful source, try looking for
184 184 # its class definition instead.
185 if hasattr(obj, '__class__'):
186 try:
187 src = inspect.getsource(obj.__class__)
188 except TypeError:
189 return None
185 try:
186 src = inspect.getsource(obj.__class__)
187 except (OSError, TypeError):
188 return None
189 except OSError:
190 return None
190 191
191 192 return src
192 193
193 194
194 195 def is_simple_callable(obj):
195 196 """True if obj is a function ()"""
196 197 return (inspect.isfunction(obj) or inspect.ismethod(obj) or \
197 198 isinstance(obj, _builtin_func_type) or isinstance(obj, _builtin_meth_type))
198 199
199 200 @undoc
200 201 def getargspec(obj):
201 202 """Wrapper around :func:`inspect.getfullargspec`
202 203
203 204 In addition to functions and methods, this can also handle objects with a
204 205 ``__call__`` attribute.
205 206
206 207 DEPRECATED: Deprecated since 7.10. Do not use, will be removed.
207 208 """
208 209
209 210 warnings.warn('`getargspec` function is deprecated as of IPython 7.10'
210 211 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
211 212
212 213 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
213 214 obj = obj.__call__
214 215
215 216 return inspect.getfullargspec(obj)
216 217
217 218 @undoc
218 219 def format_argspec(argspec):
219 220 """Format argspect, convenience wrapper around inspect's.
220 221
221 222 This takes a dict instead of ordered arguments and calls
222 223 inspect.format_argspec with the arguments in the necessary order.
223 224
224 225 DEPRECATED: Do not use; will be removed in future versions.
225 226 """
226 227
227 228 warnings.warn('`format_argspec` function is deprecated as of IPython 7.10'
228 229 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
229 230
230 231
231 232 return inspect.formatargspec(argspec['args'], argspec['varargs'],
232 233 argspec['varkw'], argspec['defaults'])
233 234
234 235 @undoc
235 236 def call_tip(oinfo, format_call=True):
236 237 """DEPRECATED. Extract call tip data from an oinfo dict.
237 238 """
238 239 warnings.warn('`call_tip` function is deprecated as of IPython 6.0'
239 240 'and will be removed in future versions.', DeprecationWarning, stacklevel=2)
240 241 # Get call definition
241 242 argspec = oinfo.get('argspec')
242 243 if argspec is None:
243 244 call_line = None
244 245 else:
245 246 # Callable objects will have 'self' as their first argument, prune
246 247 # it out if it's there for clarity (since users do *not* pass an
247 248 # extra first argument explicitly).
248 249 try:
249 250 has_self = argspec['args'][0] == 'self'
250 251 except (KeyError, IndexError):
251 252 pass
252 253 else:
253 254 if has_self:
254 255 argspec['args'] = argspec['args'][1:]
255 256
256 257 call_line = oinfo['name']+format_argspec(argspec)
257 258
258 259 # Now get docstring.
259 260 # The priority is: call docstring, constructor docstring, main one.
260 261 doc = oinfo.get('call_docstring')
261 262 if doc is None:
262 263 doc = oinfo.get('init_docstring')
263 264 if doc is None:
264 265 doc = oinfo.get('docstring','')
265 266
266 267 return call_line, doc
267 268
268 269
269 270 def _get_wrapped(obj):
270 271 """Get the original object if wrapped in one or more @decorators
271 272
272 273 Some objects automatically construct similar objects on any unrecognised
273 274 attribute access (e.g. unittest.mock.call). To protect against infinite loops,
274 275 this will arbitrarily cut off after 100 levels of obj.__wrapped__
275 276 attribute access. --TK, Jan 2016
276 277 """
277 278 orig_obj = obj
278 279 i = 0
279 280 while safe_hasattr(obj, '__wrapped__'):
280 281 obj = obj.__wrapped__
281 282 i += 1
282 283 if i > 100:
283 284 # __wrapped__ is probably a lie, so return the thing we started with
284 285 return orig_obj
285 286 return obj
286 287
287 288 def find_file(obj) -> str:
288 289 """Find the absolute path to the file where an object was defined.
289 290
290 291 This is essentially a robust wrapper around `inspect.getabsfile`.
291 292
292 293 Returns None if no file can be found.
293 294
294 295 Parameters
295 296 ----------
296 297 obj : any Python object
297 298
298 299 Returns
299 300 -------
300 301 fname : str
301 302 The absolute path to the file where the object was defined.
302 303 """
303 304 obj = _get_wrapped(obj)
304 305
305 306 fname = None
306 307 try:
307 308 fname = inspect.getabsfile(obj)
308 309 except TypeError:
309 310 # For an instance, the file that matters is where its class was
310 311 # declared.
311 if hasattr(obj, '__class__'):
312 try:
313 fname = inspect.getabsfile(obj.__class__)
314 except TypeError:
315 # Can happen for builtins
316 pass
317 except:
312 try:
313 fname = inspect.getabsfile(obj.__class__)
314 except (OSError, TypeError):
315 # Can happen for builtins
316 pass
317 except OSError:
318 318 pass
319
319 320 return cast_unicode(fname)
320 321
321 322
322 323 def find_source_lines(obj):
323 324 """Find the line number in a file where an object was defined.
324 325
325 326 This is essentially a robust wrapper around `inspect.getsourcelines`.
326 327
327 328 Returns None if no file can be found.
328 329
329 330 Parameters
330 331 ----------
331 332 obj : any Python object
332 333
333 334 Returns
334 335 -------
335 336 lineno : int
336 337 The line number where the object definition starts.
337 338 """
338 339 obj = _get_wrapped(obj)
339 340
340 341 try:
342 lineno = inspect.getsourcelines(obj)[1]
343 except TypeError:
344 # For instances, try the class object like getsource() does
341 345 try:
342 lineno = inspect.getsourcelines(obj)[1]
343 except TypeError:
344 # For instances, try the class object like getsource() does
345 if hasattr(obj, '__class__'):
346 lineno = inspect.getsourcelines(obj.__class__)[1]
347 else:
348 lineno = None
349 except:
346 lineno = inspect.getsourcelines(obj.__class__)[1]
347 except (OSError, TypeError):
348 return None
349 except OSError:
350 350 return None
351 351
352 352 return lineno
353 353
354 354 class Inspector(Colorable):
355 355
356 356 def __init__(self, color_table=InspectColors,
357 357 code_color_table=PyColorize.ANSICodeColors,
358 358 scheme=None,
359 359 str_detail_level=0,
360 360 parent=None, config=None):
361 361 super(Inspector, self).__init__(parent=parent, config=config)
362 362 self.color_table = color_table
363 363 self.parser = PyColorize.Parser(out='str', parent=self, style=scheme)
364 364 self.format = self.parser.format
365 365 self.str_detail_level = str_detail_level
366 366 self.set_active_scheme(scheme)
367 367
368 368 def _getdef(self,obj,oname='') -> Union[str,None]:
369 369 """Return the call signature for any callable object.
370 370
371 371 If any exception is generated, None is returned instead and the
372 372 exception is suppressed."""
373 373 try:
374 374 return _render_signature(signature(obj), oname)
375 375 except:
376 376 return None
377 377
378 378 def __head(self,h) -> str:
379 379 """Return a header string with proper colors."""
380 380 return '%s%s%s' % (self.color_table.active_colors.header,h,
381 381 self.color_table.active_colors.normal)
382 382
383 383 def set_active_scheme(self, scheme):
384 384 if scheme is not None:
385 385 self.color_table.set_active_scheme(scheme)
386 386 self.parser.color_table.set_active_scheme(scheme)
387 387
388 388 def noinfo(self, msg, oname):
389 389 """Generic message when no information is found."""
390 390 print('No %s found' % msg, end=' ')
391 391 if oname:
392 392 print('for %s' % oname)
393 393 else:
394 394 print()
395 395
396 396 def pdef(self, obj, oname=''):
397 397 """Print the call signature for any callable object.
398 398
399 399 If the object is a class, print the constructor information."""
400 400
401 401 if not callable(obj):
402 402 print('Object is not callable.')
403 403 return
404 404
405 405 header = ''
406 406
407 407 if inspect.isclass(obj):
408 408 header = self.__head('Class constructor information:\n')
409 409
410 410
411 411 output = self._getdef(obj,oname)
412 412 if output is None:
413 413 self.noinfo('definition header',oname)
414 414 else:
415 415 print(header,self.format(output), end=' ')
416 416
417 417 # In Python 3, all classes are new-style, so they all have __init__.
418 418 @skip_doctest
419 419 def pdoc(self, obj, oname='', formatter=None):
420 420 """Print the docstring for any object.
421 421
422 422 Optional:
423 423 -formatter: a function to run the docstring through for specially
424 424 formatted docstrings.
425 425
426 426 Examples
427 427 --------
428 428
429 429 In [1]: class NoInit:
430 430 ...: pass
431 431
432 432 In [2]: class NoDoc:
433 433 ...: def __init__(self):
434 434 ...: pass
435 435
436 436 In [3]: %pdoc NoDoc
437 437 No documentation found for NoDoc
438 438
439 439 In [4]: %pdoc NoInit
440 440 No documentation found for NoInit
441 441
442 442 In [5]: obj = NoInit()
443 443
444 444 In [6]: %pdoc obj
445 445 No documentation found for obj
446 446
447 447 In [5]: obj2 = NoDoc()
448 448
449 449 In [6]: %pdoc obj2
450 450 No documentation found for obj2
451 451 """
452 452
453 453 head = self.__head # For convenience
454 454 lines = []
455 455 ds = getdoc(obj)
456 456 if formatter:
457 457 ds = formatter(ds).get('plain/text', ds)
458 458 if ds:
459 459 lines.append(head("Class docstring:"))
460 460 lines.append(indent(ds))
461 461 if inspect.isclass(obj) and hasattr(obj, '__init__'):
462 462 init_ds = getdoc(obj.__init__)
463 463 if init_ds is not None:
464 464 lines.append(head("Init docstring:"))
465 465 lines.append(indent(init_ds))
466 466 elif hasattr(obj,'__call__'):
467 467 call_ds = getdoc(obj.__call__)
468 468 if call_ds:
469 469 lines.append(head("Call docstring:"))
470 470 lines.append(indent(call_ds))
471 471
472 472 if not lines:
473 473 self.noinfo('documentation',oname)
474 474 else:
475 475 page.page('\n'.join(lines))
476 476
477 477 def psource(self, obj, oname=''):
478 478 """Print the source code for an object."""
479 479
480 480 # Flush the source cache because inspect can return out-of-date source
481 481 linecache.checkcache()
482 482 try:
483 483 src = getsource(obj, oname=oname)
484 484 except Exception:
485 485 src = None
486 486
487 487 if src is None:
488 488 self.noinfo('source', oname)
489 489 else:
490 490 page.page(self.format(src))
491 491
492 492 def pfile(self, obj, oname=''):
493 493 """Show the whole file where an object was defined."""
494 494
495 495 lineno = find_source_lines(obj)
496 496 if lineno is None:
497 497 self.noinfo('file', oname)
498 498 return
499 499
500 500 ofile = find_file(obj)
501 501 # run contents of file through pager starting at line where the object
502 502 # is defined, as long as the file isn't binary and is actually on the
503 503 # filesystem.
504 504 if ofile.endswith(('.so', '.dll', '.pyd')):
505 505 print('File %r is binary, not printing.' % ofile)
506 506 elif not os.path.isfile(ofile):
507 507 print('File %r does not exist, not printing.' % ofile)
508 508 else:
509 509 # Print only text files, not extension binaries. Note that
510 510 # getsourcelines returns lineno with 1-offset and page() uses
511 511 # 0-offset, so we must adjust.
512 512 page.page(self.format(openpy.read_py_file(ofile, skip_encoding_cookie=False)), lineno - 1)
513 513
514 514
515 515 def _mime_format(self, text:str, formatter=None) -> dict:
516 516 """Return a mime bundle representation of the input text.
517 517
518 518 - if `formatter` is None, the returned mime bundle has
519 519 a `text/plain` field, with the input text.
520 520 a `text/html` field with a `<pre>` tag containing the input text.
521 521
522 522 - if `formatter` is not None, it must be a callable transforming the
523 523 input text into a mime bundle. Default values for `text/plain` and
524 524 `text/html` representations are the ones described above.
525 525
526 526 Note:
527 527
528 528 Formatters returning strings are supported but this behavior is deprecated.
529 529
530 530 """
531 531 defaults = {
532 532 'text/plain': text,
533 533 'text/html': '<pre>' + text + '</pre>'
534 534 }
535 535
536 536 if formatter is None:
537 537 return defaults
538 538 else:
539 539 formatted = formatter(text)
540 540
541 541 if not isinstance(formatted, dict):
542 542 # Handle the deprecated behavior of a formatter returning
543 543 # a string instead of a mime bundle.
544 544 return {
545 545 'text/plain': formatted,
546 546 'text/html': '<pre>' + formatted + '</pre>'
547 547 }
548 548
549 549 else:
550 550 return dict(defaults, **formatted)
551 551
552 552
553 553 def format_mime(self, bundle):
554 554
555 555 text_plain = bundle['text/plain']
556 556
557 557 text = ''
558 558 heads, bodies = list(zip(*text_plain))
559 559 _len = max(len(h) for h in heads)
560 560
561 561 for head, body in zip(heads, bodies):
562 562 body = body.strip('\n')
563 563 delim = '\n' if '\n' in body else ' '
564 564 text += self.__head(head+':') + (_len - len(head))*' ' +delim + body +'\n'
565 565
566 566 bundle['text/plain'] = text
567 567 return bundle
568 568
569 569 def _get_info(self, obj, oname='', formatter=None, info=None, detail_level=0):
570 570 """Retrieve an info dict and format it.
571 571
572 572 Parameters
573 573 ==========
574 574
575 575 obj: any
576 576 Object to inspect and return info from
577 577 oname: str (default: ''):
578 578 Name of the variable pointing to `obj`.
579 579 formatter: callable
580 580 info:
581 581 already computed information
582 582 detail_level: integer
583 583 Granularity of detail level, if set to 1, give more information.
584 584 """
585 585
586 586 info = self._info(obj, oname=oname, info=info, detail_level=detail_level)
587 587
588 588 _mime = {
589 589 'text/plain': [],
590 590 'text/html': '',
591 591 }
592 592
593 593 def append_field(bundle, title:str, key:str, formatter=None):
594 594 field = info[key]
595 595 if field is not None:
596 596 formatted_field = self._mime_format(field, formatter)
597 597 bundle['text/plain'].append((title, formatted_field['text/plain']))
598 598 bundle['text/html'] += '<h1>' + title + '</h1>\n' + formatted_field['text/html'] + '\n'
599 599
600 600 def code_formatter(text):
601 601 return {
602 602 'text/plain': self.format(text),
603 603 'text/html': pylight(text)
604 604 }
605 605
606 606 if info['isalias']:
607 607 append_field(_mime, 'Repr', 'string_form')
608 608
609 609 elif info['ismagic']:
610 610 if detail_level > 0:
611 611 append_field(_mime, 'Source', 'source', code_formatter)
612 612 else:
613 613 append_field(_mime, 'Docstring', 'docstring', formatter)
614 614 append_field(_mime, 'File', 'file')
615 615
616 616 elif info['isclass'] or is_simple_callable(obj):
617 617 # Functions, methods, classes
618 618 append_field(_mime, 'Signature', 'definition', code_formatter)
619 619 append_field(_mime, 'Init signature', 'init_definition', code_formatter)
620 620 append_field(_mime, 'Docstring', 'docstring', formatter)
621 621 if detail_level > 0 and info['source']:
622 622 append_field(_mime, 'Source', 'source', code_formatter)
623 623 else:
624 624 append_field(_mime, 'Init docstring', 'init_docstring', formatter)
625 625
626 626 append_field(_mime, 'File', 'file')
627 627 append_field(_mime, 'Type', 'type_name')
628 628 append_field(_mime, 'Subclasses', 'subclasses')
629 629
630 630 else:
631 631 # General Python objects
632 632 append_field(_mime, 'Signature', 'definition', code_formatter)
633 633 append_field(_mime, 'Call signature', 'call_def', code_formatter)
634 634 append_field(_mime, 'Type', 'type_name')
635 635 append_field(_mime, 'String form', 'string_form')
636 636
637 637 # Namespace
638 638 if info['namespace'] != 'Interactive':
639 639 append_field(_mime, 'Namespace', 'namespace')
640 640
641 641 append_field(_mime, 'Length', 'length')
642 642 append_field(_mime, 'File', 'file')
643 643
644 644 # Source or docstring, depending on detail level and whether
645 645 # source found.
646 646 if detail_level > 0 and info['source']:
647 647 append_field(_mime, 'Source', 'source', code_formatter)
648 648 else:
649 649 append_field(_mime, 'Docstring', 'docstring', formatter)
650 650
651 651 append_field(_mime, 'Class docstring', 'class_docstring', formatter)
652 652 append_field(_mime, 'Init docstring', 'init_docstring', formatter)
653 653 append_field(_mime, 'Call docstring', 'call_docstring', formatter)
654 654
655 655
656 656 return self.format_mime(_mime)
657 657
658 658 def pinfo(self, obj, oname='', formatter=None, info=None, detail_level=0, enable_html_pager=True):
659 659 """Show detailed information about an object.
660 660
661 661 Optional arguments:
662 662
663 663 - oname: name of the variable pointing to the object.
664 664
665 665 - formatter: callable (optional)
666 666 A special formatter for docstrings.
667 667
668 668 The formatter is a callable that takes a string as an input
669 669 and returns either a formatted string or a mime type bundle
670 670 in the form of a dictionary.
671 671
672 672 Although the support of custom formatter returning a string
673 673 instead of a mime type bundle is deprecated.
674 674
675 675 - info: a structure with some information fields which may have been
676 676 precomputed already.
677 677
678 678 - detail_level: if set to 1, more information is given.
679 679 """
680 680 info = self._get_info(obj, oname, formatter, info, detail_level)
681 681 if not enable_html_pager:
682 682 del info['text/html']
683 683 page.page(info)
684 684
685 685 def info(self, obj, oname='', formatter=None, info=None, detail_level=0):
686 686 """DEPRECATED. Compute a dict with detailed information about an object.
687 687 """
688 688 if formatter is not None:
689 689 warnings.warn('The `formatter` keyword argument to `Inspector.info`'
690 690 'is deprecated as of IPython 5.0 and will have no effects.',
691 691 DeprecationWarning, stacklevel=2)
692 692 return self._info(obj, oname=oname, info=info, detail_level=detail_level)
693 693
694 694 def _info(self, obj, oname='', info=None, detail_level=0) -> dict:
695 695 """Compute a dict with detailed information about an object.
696 696
697 697 Parameters
698 698 ==========
699 699
700 700 obj: any
701 701 An object to find information about
702 702 oname: str (default: ''):
703 703 Name of the variable pointing to `obj`.
704 704 info: (default: None)
705 705 A struct (dict like with attr access) with some information fields
706 706 which may have been precomputed already.
707 707 detail_level: int (default:0)
708 708 If set to 1, more information is given.
709 709
710 710 Returns
711 711 =======
712 712
713 713 An object info dict with known fields from `info_fields`. Keys are
714 714 strings, values are string or None.
715 715 """
716 716
717 717 if info is None:
718 718 ismagic = False
719 719 isalias = False
720 720 ospace = ''
721 721 else:
722 722 ismagic = info.ismagic
723 723 isalias = info.isalias
724 724 ospace = info.namespace
725 725
726 726 # Get docstring, special-casing aliases:
727 727 if isalias:
728 728 if not callable(obj):
729 729 try:
730 730 ds = "Alias to the system command:\n %s" % obj[1]
731 731 except:
732 732 ds = "Alias: " + str(obj)
733 733 else:
734 734 ds = "Alias to " + str(obj)
735 735 if obj.__doc__:
736 736 ds += "\nDocstring:\n" + obj.__doc__
737 737 else:
738 738 ds = getdoc(obj)
739 739 if ds is None:
740 740 ds = '<no docstring>'
741 741
742 742 # store output in a dict, we initialize it here and fill it as we go
743 743 out = dict(name=oname, found=True, isalias=isalias, ismagic=ismagic, subclasses=None)
744 744
745 745 string_max = 200 # max size of strings to show (snipped if longer)
746 746 shalf = int((string_max - 5) / 2)
747 747
748 748 if ismagic:
749 749 out['type_name'] = 'Magic function'
750 750 elif isalias:
751 751 out['type_name'] = 'System alias'
752 752 else:
753 753 out['type_name'] = type(obj).__name__
754 754
755 755 try:
756 756 bclass = obj.__class__
757 757 out['base_class'] = str(bclass)
758 758 except:
759 759 pass
760 760
761 761 # String form, but snip if too long in ? form (full in ??)
762 762 if detail_level >= self.str_detail_level:
763 763 try:
764 764 ostr = str(obj)
765 765 str_head = 'string_form'
766 766 if not detail_level and len(ostr)>string_max:
767 767 ostr = ostr[:shalf] + ' <...> ' + ostr[-shalf:]
768 768 ostr = ("\n" + " " * len(str_head.expandtabs())).\
769 769 join(q.strip() for q in ostr.split("\n"))
770 770 out[str_head] = ostr
771 771 except:
772 772 pass
773 773
774 774 if ospace:
775 775 out['namespace'] = ospace
776 776
777 777 # Length (for strings and lists)
778 778 try:
779 779 out['length'] = str(len(obj))
780 780 except Exception:
781 781 pass
782 782
783 783 # Filename where object was defined
784 784 binary_file = False
785 785 fname = find_file(obj)
786 786 if fname is None:
787 787 # if anything goes wrong, we don't want to show source, so it's as
788 788 # if the file was binary
789 789 binary_file = True
790 790 else:
791 791 if fname.endswith(('.so', '.dll', '.pyd')):
792 792 binary_file = True
793 793 elif fname.endswith('<string>'):
794 794 fname = 'Dynamically generated function. No source code available.'
795 795 out['file'] = compress_user(fname)
796 796
797 797 # Original source code for a callable, class or property.
798 798 if detail_level:
799 799 # Flush the source cache because inspect can return out-of-date
800 800 # source
801 801 linecache.checkcache()
802 802 try:
803 803 if isinstance(obj, property) or not binary_file:
804 804 src = getsource(obj, oname)
805 805 if src is not None:
806 806 src = src.rstrip()
807 807 out['source'] = src
808 808
809 809 except Exception:
810 810 pass
811 811
812 812 # Add docstring only if no source is to be shown (avoid repetitions).
813 813 if ds and not self._source_contains_docstring(out.get('source'), ds):
814 814 out['docstring'] = ds
815 815
816 816 # Constructor docstring for classes
817 817 if inspect.isclass(obj):
818 818 out['isclass'] = True
819 819
820 820 # get the init signature:
821 821 try:
822 822 init_def = self._getdef(obj, oname)
823 823 except AttributeError:
824 824 init_def = None
825 825
826 826 # get the __init__ docstring
827 827 try:
828 828 obj_init = obj.__init__
829 829 except AttributeError:
830 830 init_ds = None
831 831 else:
832 832 if init_def is None:
833 833 # Get signature from init if top-level sig failed.
834 834 # Can happen for built-in types (list, etc.).
835 835 try:
836 836 init_def = self._getdef(obj_init, oname)
837 837 except AttributeError:
838 838 pass
839 839 init_ds = getdoc(obj_init)
840 840 # Skip Python's auto-generated docstrings
841 841 if init_ds == _object_init_docstring:
842 842 init_ds = None
843 843
844 844 if init_def:
845 845 out['init_definition'] = init_def
846 846
847 847 if init_ds:
848 848 out['init_docstring'] = init_ds
849 849
850 850 names = [sub.__name__ for sub in type.__subclasses__(obj)]
851 851 if len(names) < 10:
852 852 all_names = ', '.join(names)
853 853 else:
854 854 all_names = ', '.join(names[:10]+['...'])
855 855 out['subclasses'] = all_names
856 856 # and class docstring for instances:
857 857 else:
858 858 # reconstruct the function definition and print it:
859 859 defln = self._getdef(obj, oname)
860 860 if defln:
861 861 out['definition'] = defln
862 862
863 863 # First, check whether the instance docstring is identical to the
864 864 # class one, and print it separately if they don't coincide. In
865 865 # most cases they will, but it's nice to print all the info for
866 866 # objects which use instance-customized docstrings.
867 867 if ds:
868 868 try:
869 869 cls = getattr(obj,'__class__')
870 870 except:
871 871 class_ds = None
872 872 else:
873 873 class_ds = getdoc(cls)
874 874 # Skip Python's auto-generated docstrings
875 875 if class_ds in _builtin_type_docstrings:
876 876 class_ds = None
877 877 if class_ds and ds != class_ds:
878 878 out['class_docstring'] = class_ds
879 879
880 880 # Next, try to show constructor docstrings
881 881 try:
882 882 init_ds = getdoc(obj.__init__)
883 883 # Skip Python's auto-generated docstrings
884 884 if init_ds == _object_init_docstring:
885 885 init_ds = None
886 886 except AttributeError:
887 887 init_ds = None
888 888 if init_ds:
889 889 out['init_docstring'] = init_ds
890 890
891 891 # Call form docstring for callable instances
892 892 if safe_hasattr(obj, '__call__') and not is_simple_callable(obj):
893 893 call_def = self._getdef(obj.__call__, oname)
894 894 if call_def and (call_def != out.get('definition')):
895 895 # it may never be the case that call def and definition differ,
896 896 # but don't include the same signature twice
897 897 out['call_def'] = call_def
898 898 call_ds = getdoc(obj.__call__)
899 899 # Skip Python's auto-generated docstrings
900 900 if call_ds == _func_call_docstring:
901 901 call_ds = None
902 902 if call_ds:
903 903 out['call_docstring'] = call_ds
904 904
905 905 return object_info(**out)
906 906
907 907 @staticmethod
908 908 def _source_contains_docstring(src, doc):
909 909 """
910 910 Check whether the source *src* contains the docstring *doc*.
911 911
912 912 This is is helper function to skip displaying the docstring if the
913 913 source already contains it, avoiding repetition of information.
914 914 """
915 915 try:
916 916 def_node, = ast.parse(dedent(src)).body
917 917 return ast.get_docstring(def_node) == doc
918 918 except Exception:
919 919 # The source can become invalid or even non-existent (because it
920 920 # is re-fetched from the source file) so the above code fail in
921 921 # arbitrary ways.
922 922 return False
923 923
924 924 def psearch(self,pattern,ns_table,ns_search=[],
925 925 ignore_case=False,show_all=False, *, list_types=False):
926 926 """Search namespaces with wildcards for objects.
927 927
928 928 Arguments:
929 929
930 930 - pattern: string containing shell-like wildcards to use in namespace
931 931 searches and optionally a type specification to narrow the search to
932 932 objects of that type.
933 933
934 934 - ns_table: dict of name->namespaces for search.
935 935
936 936 Optional arguments:
937 937
938 938 - ns_search: list of namespace names to include in search.
939 939
940 940 - ignore_case(False): make the search case-insensitive.
941 941
942 942 - show_all(False): show all names, including those starting with
943 943 underscores.
944 944
945 945 - list_types(False): list all available object types for object matching.
946 946 """
947 947 #print 'ps pattern:<%r>' % pattern # dbg
948 948
949 949 # defaults
950 950 type_pattern = 'all'
951 951 filter = ''
952 952
953 953 # list all object types
954 954 if list_types:
955 955 page.page('\n'.join(sorted(typestr2type)))
956 956 return
957 957
958 958 cmds = pattern.split()
959 959 len_cmds = len(cmds)
960 960 if len_cmds == 1:
961 961 # Only filter pattern given
962 962 filter = cmds[0]
963 963 elif len_cmds == 2:
964 964 # Both filter and type specified
965 965 filter,type_pattern = cmds
966 966 else:
967 967 raise ValueError('invalid argument string for psearch: <%s>' %
968 968 pattern)
969 969
970 970 # filter search namespaces
971 971 for name in ns_search:
972 972 if name not in ns_table:
973 973 raise ValueError('invalid namespace <%s>. Valid names: %s' %
974 974 (name,ns_table.keys()))
975 975
976 976 #print 'type_pattern:',type_pattern # dbg
977 977 search_result, namespaces_seen = set(), set()
978 978 for ns_name in ns_search:
979 979 ns = ns_table[ns_name]
980 980 # Normally, locals and globals are the same, so we just check one.
981 981 if id(ns) in namespaces_seen:
982 982 continue
983 983 namespaces_seen.add(id(ns))
984 984 tmp_res = list_namespace(ns, type_pattern, filter,
985 985 ignore_case=ignore_case, show_all=show_all)
986 986 search_result.update(tmp_res)
987 987
988 988 page.page('\n'.join(sorted(search_result)))
989 989
990 990
991 991 def _render_signature(obj_signature, obj_name) -> str:
992 992 """
993 993 This was mostly taken from inspect.Signature.__str__.
994 994 Look there for the comments.
995 995 The only change is to add linebreaks when this gets too long.
996 996 """
997 997 result = []
998 998 pos_only = False
999 999 kw_only = True
1000 1000 for param in obj_signature.parameters.values():
1001 1001 if param.kind == inspect._POSITIONAL_ONLY:
1002 1002 pos_only = True
1003 1003 elif pos_only:
1004 1004 result.append('/')
1005 1005 pos_only = False
1006 1006
1007 1007 if param.kind == inspect._VAR_POSITIONAL:
1008 1008 kw_only = False
1009 1009 elif param.kind == inspect._KEYWORD_ONLY and kw_only:
1010 1010 result.append('*')
1011 1011 kw_only = False
1012 1012
1013 1013 result.append(str(param))
1014 1014
1015 1015 if pos_only:
1016 1016 result.append('/')
1017 1017
1018 1018 # add up name, parameters, braces (2), and commas
1019 1019 if len(obj_name) + sum(len(r) + 2 for r in result) > 75:
1020 1020 # This doesn’t fit behind β€œSignature: ” in an inspect window.
1021 1021 rendered = '{}(\n{})'.format(obj_name, ''.join(
1022 1022 ' {},\n'.format(r) for r in result)
1023 1023 )
1024 1024 else:
1025 1025 rendered = '{}({})'.format(obj_name, ', '.join(result))
1026 1026
1027 1027 if obj_signature.return_annotation is not inspect._empty:
1028 1028 anno = inspect.formatannotation(obj_signature.return_annotation)
1029 1029 rendered += ' -> {}'.format(anno)
1030 1030
1031 1031 return rendered
@@ -1,1111 +1,1124 b''
1 1 # encoding: utf-8
2 2 """Tests for the IPython tab-completion machinery."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import sys
9 9 import textwrap
10 10 import unittest
11 11
12 12 from contextlib import contextmanager
13 13
14 14 import nose.tools as nt
15 import pytest
15 16
16 17 from traitlets.config.loader import Config
17 18 from IPython import get_ipython
18 19 from IPython.core import completer
19 20 from IPython.external import decorators
20 21 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
21 22 from IPython.utils.generics import complete_object
22 23 from IPython.testing import decorators as dec
23 24
24 25 from IPython.core.completer import (
25 26 Completion,
26 27 provisionalcompleter,
27 28 match_dict_keys,
28 29 _deduplicate_completions,
29 30 )
30 31 from nose.tools import assert_in, assert_not_in
31 32
33 if sys.version_info >= (3, 10):
34 import jedi
35 from pkg_resources import parse_version
36
37 # Requires https://github.com/davidhalter/jedi/pull/1795
38 jedi_issue = parse_version(jedi.__version__) <= parse_version("0.18.0")
39 else:
40 jedi_issue = False
41
32 42 # -----------------------------------------------------------------------------
33 43 # Test functions
34 44 # -----------------------------------------------------------------------------
35 45
36 46
37 47 @contextmanager
38 48 def greedy_completion():
39 49 ip = get_ipython()
40 50 greedy_original = ip.Completer.greedy
41 51 try:
42 52 ip.Completer.greedy = True
43 53 yield
44 54 finally:
45 55 ip.Completer.greedy = greedy_original
46 56
47 57
48 58 def test_protect_filename():
49 59 if sys.platform == "win32":
50 60 pairs = [
51 61 ("abc", "abc"),
52 62 (" abc", '" abc"'),
53 63 ("a bc", '"a bc"'),
54 64 ("a bc", '"a bc"'),
55 65 (" bc", '" bc"'),
56 66 ]
57 67 else:
58 68 pairs = [
59 69 ("abc", "abc"),
60 70 (" abc", r"\ abc"),
61 71 ("a bc", r"a\ bc"),
62 72 ("a bc", r"a\ \ bc"),
63 73 (" bc", r"\ \ bc"),
64 74 # On posix, we also protect parens and other special characters.
65 75 ("a(bc", r"a\(bc"),
66 76 ("a)bc", r"a\)bc"),
67 77 ("a( )bc", r"a\(\ \)bc"),
68 78 ("a[1]bc", r"a\[1\]bc"),
69 79 ("a{1}bc", r"a\{1\}bc"),
70 80 ("a#bc", r"a\#bc"),
71 81 ("a?bc", r"a\?bc"),
72 82 ("a=bc", r"a\=bc"),
73 83 ("a\\bc", r"a\\bc"),
74 84 ("a|bc", r"a\|bc"),
75 85 ("a;bc", r"a\;bc"),
76 86 ("a:bc", r"a\:bc"),
77 87 ("a'bc", r"a\'bc"),
78 88 ("a*bc", r"a\*bc"),
79 89 ('a"bc', r"a\"bc"),
80 90 ("a^bc", r"a\^bc"),
81 91 ("a&bc", r"a\&bc"),
82 92 ]
83 93 # run the actual tests
84 94 for s1, s2 in pairs:
85 95 s1p = completer.protect_filename(s1)
86 96 nt.assert_equal(s1p, s2)
87 97
88 98
89 99 def check_line_split(splitter, test_specs):
90 100 for part1, part2, split in test_specs:
91 101 cursor_pos = len(part1)
92 102 line = part1 + part2
93 103 out = splitter.split_line(line, cursor_pos)
94 104 nt.assert_equal(out, split)
95 105
96 106
97 107 def test_line_split():
98 108 """Basic line splitter test with default specs."""
99 109 sp = completer.CompletionSplitter()
100 110 # The format of the test specs is: part1, part2, expected answer. Parts 1
101 111 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
102 112 # was at the end of part1. So an empty part2 represents someone hitting
103 113 # tab at the end of the line, the most common case.
104 114 t = [
105 115 ("run some/scrip", "", "some/scrip"),
106 116 ("run scripts/er", "ror.py foo", "scripts/er"),
107 117 ("echo $HOM", "", "HOM"),
108 118 ("print sys.pa", "", "sys.pa"),
109 119 ("print(sys.pa", "", "sys.pa"),
110 120 ("execfile('scripts/er", "", "scripts/er"),
111 121 ("a[x.", "", "x."),
112 122 ("a[x.", "y", "x."),
113 123 ('cd "some_file/', "", "some_file/"),
114 124 ]
115 125 check_line_split(sp, t)
116 126 # Ensure splitting works OK with unicode by re-running the tests with
117 127 # all inputs turned into unicode
118 128 check_line_split(sp, [map(str, p) for p in t])
119 129
120 130
121 131 class NamedInstanceMetaclass(type):
122 132 def __getitem__(cls, item):
123 133 return cls.get_instance(item)
124 134
125 135
126 136 class NamedInstanceClass(metaclass=NamedInstanceMetaclass):
127 137 def __init__(self, name):
128 138 if not hasattr(self.__class__, "instances"):
129 139 self.__class__.instances = {}
130 140 self.__class__.instances[name] = self
131 141
132 142 @classmethod
133 143 def _ipython_key_completions_(cls):
134 144 return cls.instances.keys()
135 145
136 146 @classmethod
137 147 def get_instance(cls, name):
138 148 return cls.instances[name]
139 149
140 150
141 151 class KeyCompletable:
142 152 def __init__(self, things=()):
143 153 self.things = things
144 154
145 155 def _ipython_key_completions_(self):
146 156 return list(self.things)
147 157
148 158
149 159 class TestCompleter(unittest.TestCase):
150 160 def setUp(self):
151 161 """
152 162 We want to silence all PendingDeprecationWarning when testing the completer
153 163 """
154 164 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
155 165 self._assertwarns.__enter__()
156 166
157 167 def tearDown(self):
158 168 try:
159 169 self._assertwarns.__exit__(None, None, None)
160 170 except AssertionError:
161 171 pass
162 172
163 173 def test_custom_completion_error(self):
164 174 """Test that errors from custom attribute completers are silenced."""
165 175 ip = get_ipython()
166 176
167 177 class A:
168 178 pass
169 179
170 180 ip.user_ns["x"] = A()
171 181
172 182 @complete_object.register(A)
173 183 def complete_A(a, existing_completions):
174 184 raise TypeError("this should be silenced")
175 185
176 186 ip.complete("x.")
177 187
178 188 def test_custom_completion_ordering(self):
179 189 """Test that errors from custom attribute completers are silenced."""
180 190 ip = get_ipython()
181 191
182 192 _, matches = ip.complete('in')
183 193 assert matches.index('input') < matches.index('int')
184 194
185 195 def complete_example(a):
186 196 return ['example2', 'example1']
187 197
188 198 ip.Completer.custom_completers.add_re('ex*', complete_example)
189 199 _, matches = ip.complete('ex')
190 200 assert matches.index('example2') < matches.index('example1')
191 201
192 202 def test_unicode_completions(self):
193 203 ip = get_ipython()
194 204 # Some strings that trigger different types of completion. Check them both
195 205 # in str and unicode forms
196 206 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
197 207 for t in s + list(map(str, s)):
198 208 # We don't need to check exact completion values (they may change
199 209 # depending on the state of the namespace, but at least no exceptions
200 210 # should be thrown and the return value should be a pair of text, list
201 211 # values.
202 212 text, matches = ip.complete(t)
203 213 nt.assert_true(isinstance(text, str))
204 214 nt.assert_true(isinstance(matches, list))
205 215
206 216 def test_latex_completions(self):
207 217 from IPython.core.latex_symbols import latex_symbols
208 218 import random
209 219
210 220 ip = get_ipython()
211 221 # Test some random unicode symbols
212 222 keys = random.sample(latex_symbols.keys(), 10)
213 223 for k in keys:
214 224 text, matches = ip.complete(k)
215 225 nt.assert_equal(len(matches), 1)
216 226 nt.assert_equal(text, k)
217 227 nt.assert_equal(matches[0], latex_symbols[k])
218 228 # Test a more complex line
219 229 text, matches = ip.complete("print(\\alpha")
220 230 nt.assert_equal(text, "\\alpha")
221 231 nt.assert_equal(matches[0], latex_symbols["\\alpha"])
222 232 # Test multiple matching latex symbols
223 233 text, matches = ip.complete("\\al")
224 234 nt.assert_in("\\alpha", matches)
225 235 nt.assert_in("\\aleph", matches)
226 236
227 237 def test_latex_no_results(self):
228 238 """
229 239 forward latex should really return nothing in either field if nothing is found.
230 240 """
231 241 ip = get_ipython()
232 242 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
233 243 nt.assert_equal(text, "")
234 244 nt.assert_equal(matches, [])
235 245
236 246 def test_back_latex_completion(self):
237 247 ip = get_ipython()
238 248
239 249 # do not return more than 1 matches fro \beta, only the latex one.
240 250 name, matches = ip.complete("\\Ξ²")
241 251 nt.assert_equal(matches, ['\\beta'])
242 252
243 253 def test_back_unicode_completion(self):
244 254 ip = get_ipython()
245 255
246 256 name, matches = ip.complete("\\β…€")
247 257 nt.assert_equal(matches, ["\\ROMAN NUMERAL FIVE"])
248 258
249 259 def test_forward_unicode_completion(self):
250 260 ip = get_ipython()
251 261
252 262 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
253 263 nt.assert_equal(len(matches), 1)
254 264 nt.assert_equal(matches[0], "β…€")
255 265
256 266 @nt.nottest # now we have a completion for \jmath
257 267 @decorators.knownfailureif(
258 268 sys.platform == "win32", "Fails if there is a C:\\j... path"
259 269 )
260 270 def test_no_ascii_back_completion(self):
261 271 ip = get_ipython()
262 272 with TemporaryWorkingDirectory(): # Avoid any filename completions
263 273 # single ascii letter that don't have yet completions
264 274 for letter in "jJ":
265 275 name, matches = ip.complete("\\" + letter)
266 276 nt.assert_equal(matches, [])
267 277
268 278 class CompletionSplitterTestCase(unittest.TestCase):
269 279 def setUp(self):
270 280 self.sp = completer.CompletionSplitter()
271 281
272 282 def test_delim_setting(self):
273 283 self.sp.delims = " "
274 284 nt.assert_equal(self.sp.delims, " ")
275 285 nt.assert_equal(self.sp._delim_expr, r"[\ ]")
276 286
277 287 def test_spaces(self):
278 288 """Test with only spaces as split chars."""
279 289 self.sp.delims = " "
280 290 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
281 291 check_line_split(self.sp, t)
282 292
283 293 def test_has_open_quotes1(self):
284 294 for s in ["'", "'''", "'hi' '"]:
285 295 nt.assert_equal(completer.has_open_quotes(s), "'")
286 296
287 297 def test_has_open_quotes2(self):
288 298 for s in ['"', '"""', '"hi" "']:
289 299 nt.assert_equal(completer.has_open_quotes(s), '"')
290 300
291 301 def test_has_open_quotes3(self):
292 302 for s in ["''", "''' '''", "'hi' 'ipython'"]:
293 303 nt.assert_false(completer.has_open_quotes(s))
294 304
295 305 def test_has_open_quotes4(self):
296 306 for s in ['""', '""" """', '"hi" "ipython"']:
297 307 nt.assert_false(completer.has_open_quotes(s))
298 308
299 309 @decorators.knownfailureif(
300 310 sys.platform == "win32", "abspath completions fail on Windows"
301 311 )
302 312 def test_abspath_file_completions(self):
303 313 ip = get_ipython()
304 314 with TemporaryDirectory() as tmpdir:
305 315 prefix = os.path.join(tmpdir, "foo")
306 316 suffixes = ["1", "2"]
307 317 names = [prefix + s for s in suffixes]
308 318 for n in names:
309 319 open(n, "w").close()
310 320
311 321 # Check simple completion
312 322 c = ip.complete(prefix)[1]
313 323 nt.assert_equal(c, names)
314 324
315 325 # Now check with a function call
316 326 cmd = 'a = f("%s' % prefix
317 327 c = ip.complete(prefix, cmd)[1]
318 328 comp = [prefix + s for s in suffixes]
319 329 nt.assert_equal(c, comp)
320 330
321 331 def test_local_file_completions(self):
322 332 ip = get_ipython()
323 333 with TemporaryWorkingDirectory():
324 334 prefix = "./foo"
325 335 suffixes = ["1", "2"]
326 336 names = [prefix + s for s in suffixes]
327 337 for n in names:
328 338 open(n, "w").close()
329 339
330 340 # Check simple completion
331 341 c = ip.complete(prefix)[1]
332 342 nt.assert_equal(c, names)
333 343
334 344 # Now check with a function call
335 345 cmd = 'a = f("%s' % prefix
336 346 c = ip.complete(prefix, cmd)[1]
337 347 comp = {prefix + s for s in suffixes}
338 348 nt.assert_true(comp.issubset(set(c)))
339 349
340 350 def test_quoted_file_completions(self):
341 351 ip = get_ipython()
342 352 with TemporaryWorkingDirectory():
343 353 name = "foo'bar"
344 354 open(name, "w").close()
345 355
346 356 # Don't escape Windows
347 357 escaped = name if sys.platform == "win32" else "foo\\'bar"
348 358
349 359 # Single quote matches embedded single quote
350 360 text = "open('foo"
351 361 c = ip.Completer._complete(
352 362 cursor_line=0, cursor_pos=len(text), full_text=text
353 363 )[1]
354 364 nt.assert_equal(c, [escaped])
355 365
356 366 # Double quote requires no escape
357 367 text = 'open("foo'
358 368 c = ip.Completer._complete(
359 369 cursor_line=0, cursor_pos=len(text), full_text=text
360 370 )[1]
361 371 nt.assert_equal(c, [name])
362 372
363 373 # No quote requires an escape
364 374 text = "%ls foo"
365 375 c = ip.Completer._complete(
366 376 cursor_line=0, cursor_pos=len(text), full_text=text
367 377 )[1]
368 378 nt.assert_equal(c, [escaped])
369 379
370 380 def test_all_completions_dups(self):
371 381 """
372 382 Make sure the output of `IPCompleter.all_completions` does not have
373 383 duplicated prefixes.
374 384 """
375 385 ip = get_ipython()
376 386 c = ip.Completer
377 387 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
378 388 for jedi_status in [True, False]:
379 389 with provisionalcompleter():
380 390 ip.Completer.use_jedi = jedi_status
381 391 matches = c.all_completions("TestCl")
382 392 assert matches == ['TestClass'], jedi_status
383 393 matches = c.all_completions("TestClass.")
394 if jedi_status and jedi_issue:
395 continue
384 396 assert len(matches) > 2, jedi_status
385 397 matches = c.all_completions("TestClass.a")
386 398 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
387 399
388 400 def test_jedi(self):
389 401 """
390 402 A couple of issue we had with Jedi
391 403 """
392 404 ip = get_ipython()
393 405
394 406 def _test_complete(reason, s, comp, start=None, end=None):
395 407 l = len(s)
396 408 start = start if start is not None else l
397 409 end = end if end is not None else l
398 410 with provisionalcompleter():
399 411 ip.Completer.use_jedi = True
400 412 completions = set(ip.Completer.completions(s, l))
401 413 ip.Completer.use_jedi = False
402 414 assert_in(Completion(start, end, comp), completions, reason)
403 415
404 416 def _test_not_complete(reason, s, comp):
405 417 l = len(s)
406 418 with provisionalcompleter():
407 419 ip.Completer.use_jedi = True
408 420 completions = set(ip.Completer.completions(s, l))
409 421 ip.Completer.use_jedi = False
410 422 assert_not_in(Completion(l, l, comp), completions, reason)
411 423
412 424 import jedi
413 425
414 426 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
415 427 if jedi_version > (0, 10):
416 428 yield _test_complete, "jedi >0.9 should complete and not crash", "a=1;a.", "real"
417 429 yield _test_complete, "can infer first argument", 'a=(1,"foo");a[0].', "real"
418 430 yield _test_complete, "can infer second argument", 'a=(1,"foo");a[1].', "capitalize"
419 431 yield _test_complete, "cover duplicate completions", "im", "import", 0, 2
420 432
421 433 yield _test_not_complete, "does not mix types", 'a=(1,"foo");a[0].', "capitalize"
422 434
423 435 def test_completion_have_signature(self):
424 436 """
425 437 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
426 438 """
427 439 ip = get_ipython()
428 440 with provisionalcompleter():
429 441 ip.Completer.use_jedi = True
430 442 completions = ip.Completer.completions("ope", 3)
431 443 c = next(completions) # should be `open`
432 444 ip.Completer.use_jedi = False
433 445 assert "file" in c.signature, "Signature of function was not found by completer"
434 446 assert (
435 447 "encoding" in c.signature
436 448 ), "Signature of function was not found by completer"
437 449
450 @pytest.mark.xfail(jedi_issue, reason="Known failure on jedi<=0.18.0")
438 451 def test_deduplicate_completions(self):
439 452 """
440 453 Test that completions are correctly deduplicated (even if ranges are not the same)
441 454 """
442 455 ip = get_ipython()
443 456 ip.ex(
444 457 textwrap.dedent(
445 458 """
446 459 class Z:
447 460 zoo = 1
448 461 """
449 462 )
450 463 )
451 464 with provisionalcompleter():
452 465 ip.Completer.use_jedi = True
453 466 l = list(
454 467 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
455 468 )
456 469 ip.Completer.use_jedi = False
457 470
458 471 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
459 472 assert l[0].text == "zoo" # and not `it.accumulate`
460 473
461 474 def test_greedy_completions(self):
462 475 """
463 476 Test the capability of the Greedy completer.
464 477
465 478 Most of the test here does not really show off the greedy completer, for proof
466 479 each of the text below now pass with Jedi. The greedy completer is capable of more.
467 480
468 481 See the :any:`test_dict_key_completion_contexts`
469 482
470 483 """
471 484 ip = get_ipython()
472 485 ip.ex("a=list(range(5))")
473 486 _, c = ip.complete(".", line="a[0].")
474 487 nt.assert_false(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
475 488
476 489 def _(line, cursor_pos, expect, message, completion):
477 490 with greedy_completion(), provisionalcompleter():
478 491 ip.Completer.use_jedi = False
479 492 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
480 493 nt.assert_in(expect, c, message % c)
481 494
482 495 ip.Completer.use_jedi = True
483 496 with provisionalcompleter():
484 497 completions = ip.Completer.completions(line, cursor_pos)
485 498 nt.assert_in(completion, completions)
486 499
487 500 with provisionalcompleter():
488 501 yield _, "a[0].", 5, "a[0].real", "Should have completed on a[0].: %s", Completion(
489 502 5, 5, "real"
490 503 )
491 504 yield _, "a[0].r", 6, "a[0].real", "Should have completed on a[0].r: %s", Completion(
492 505 5, 6, "real"
493 506 )
494 507
495 508 yield _, "a[0].from_", 10, "a[0].from_bytes", "Should have completed on a[0].from_: %s", Completion(
496 509 5, 10, "from_bytes"
497 510 )
498 511
499 512 def test_omit__names(self):
500 513 # also happens to test IPCompleter as a configurable
501 514 ip = get_ipython()
502 515 ip._hidden_attr = 1
503 516 ip._x = {}
504 517 c = ip.Completer
505 518 ip.ex("ip=get_ipython()")
506 519 cfg = Config()
507 520 cfg.IPCompleter.omit__names = 0
508 521 c.update_config(cfg)
509 522 with provisionalcompleter():
510 523 c.use_jedi = False
511 524 s, matches = c.complete("ip.")
512 525 nt.assert_in("ip.__str__", matches)
513 526 nt.assert_in("ip._hidden_attr", matches)
514 527
515 528 # c.use_jedi = True
516 529 # completions = set(c.completions('ip.', 3))
517 530 # nt.assert_in(Completion(3, 3, '__str__'), completions)
518 531 # nt.assert_in(Completion(3,3, "_hidden_attr"), completions)
519 532
520 533 cfg = Config()
521 534 cfg.IPCompleter.omit__names = 1
522 535 c.update_config(cfg)
523 536 with provisionalcompleter():
524 537 c.use_jedi = False
525 538 s, matches = c.complete("ip.")
526 539 nt.assert_not_in("ip.__str__", matches)
527 540 # nt.assert_in('ip._hidden_attr', matches)
528 541
529 542 # c.use_jedi = True
530 543 # completions = set(c.completions('ip.', 3))
531 544 # nt.assert_not_in(Completion(3,3,'__str__'), completions)
532 545 # nt.assert_in(Completion(3,3, "_hidden_attr"), completions)
533 546
534 547 cfg = Config()
535 548 cfg.IPCompleter.omit__names = 2
536 549 c.update_config(cfg)
537 550 with provisionalcompleter():
538 551 c.use_jedi = False
539 552 s, matches = c.complete("ip.")
540 553 nt.assert_not_in("ip.__str__", matches)
541 554 nt.assert_not_in("ip._hidden_attr", matches)
542 555
543 556 # c.use_jedi = True
544 557 # completions = set(c.completions('ip.', 3))
545 558 # nt.assert_not_in(Completion(3,3,'__str__'), completions)
546 559 # nt.assert_not_in(Completion(3,3, "_hidden_attr"), completions)
547 560
548 561 with provisionalcompleter():
549 562 c.use_jedi = False
550 563 s, matches = c.complete("ip._x.")
551 564 nt.assert_in("ip._x.keys", matches)
552 565
553 566 # c.use_jedi = True
554 567 # completions = set(c.completions('ip._x.', 6))
555 568 # nt.assert_in(Completion(6,6, "keys"), completions)
556 569
557 570 del ip._hidden_attr
558 571 del ip._x
559 572
560 573 def test_limit_to__all__False_ok(self):
561 574 """
562 575 Limit to all is deprecated, once we remove it this test can go away.
563 576 """
564 577 ip = get_ipython()
565 578 c = ip.Completer
566 579 c.use_jedi = False
567 580 ip.ex("class D: x=24")
568 581 ip.ex("d=D()")
569 582 cfg = Config()
570 583 cfg.IPCompleter.limit_to__all__ = False
571 584 c.update_config(cfg)
572 585 s, matches = c.complete("d.")
573 586 nt.assert_in("d.x", matches)
574 587
575 588 def test_get__all__entries_ok(self):
576 589 class A:
577 590 __all__ = ["x", 1]
578 591
579 592 words = completer.get__all__entries(A())
580 593 nt.assert_equal(words, ["x"])
581 594
582 595 def test_get__all__entries_no__all__ok(self):
583 596 class A:
584 597 pass
585 598
586 599 words = completer.get__all__entries(A())
587 600 nt.assert_equal(words, [])
588 601
589 602 def test_func_kw_completions(self):
590 603 ip = get_ipython()
591 604 c = ip.Completer
592 605 c.use_jedi = False
593 606 ip.ex("def myfunc(a=1,b=2): return a+b")
594 607 s, matches = c.complete(None, "myfunc(1,b")
595 608 nt.assert_in("b=", matches)
596 609 # Simulate completing with cursor right after b (pos==10):
597 610 s, matches = c.complete(None, "myfunc(1,b)", 10)
598 611 nt.assert_in("b=", matches)
599 612 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
600 613 nt.assert_in("b=", matches)
601 614 # builtin function
602 615 s, matches = c.complete(None, "min(k, k")
603 616 nt.assert_in("key=", matches)
604 617
605 618 def test_default_arguments_from_docstring(self):
606 619 ip = get_ipython()
607 620 c = ip.Completer
608 621 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
609 622 nt.assert_equal(kwd, ["key"])
610 623 # with cython type etc
611 624 kwd = c._default_arguments_from_docstring(
612 625 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
613 626 )
614 627 nt.assert_equal(kwd, ["ncall", "resume", "nsplit"])
615 628 # white spaces
616 629 kwd = c._default_arguments_from_docstring(
617 630 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
618 631 )
619 632 nt.assert_equal(kwd, ["ncall", "resume", "nsplit"])
620 633
621 634 def test_line_magics(self):
622 635 ip = get_ipython()
623 636 c = ip.Completer
624 637 s, matches = c.complete(None, "lsmag")
625 638 nt.assert_in("%lsmagic", matches)
626 639 s, matches = c.complete(None, "%lsmag")
627 640 nt.assert_in("%lsmagic", matches)
628 641
629 642 def test_cell_magics(self):
630 643 from IPython.core.magic import register_cell_magic
631 644
632 645 @register_cell_magic
633 646 def _foo_cellm(line, cell):
634 647 pass
635 648
636 649 ip = get_ipython()
637 650 c = ip.Completer
638 651
639 652 s, matches = c.complete(None, "_foo_ce")
640 653 nt.assert_in("%%_foo_cellm", matches)
641 654 s, matches = c.complete(None, "%%_foo_ce")
642 655 nt.assert_in("%%_foo_cellm", matches)
643 656
644 657 def test_line_cell_magics(self):
645 658 from IPython.core.magic import register_line_cell_magic
646 659
647 660 @register_line_cell_magic
648 661 def _bar_cellm(line, cell):
649 662 pass
650 663
651 664 ip = get_ipython()
652 665 c = ip.Completer
653 666
654 667 # The policy here is trickier, see comments in completion code. The
655 668 # returned values depend on whether the user passes %% or not explicitly,
656 669 # and this will show a difference if the same name is both a line and cell
657 670 # magic.
658 671 s, matches = c.complete(None, "_bar_ce")
659 672 nt.assert_in("%_bar_cellm", matches)
660 673 nt.assert_in("%%_bar_cellm", matches)
661 674 s, matches = c.complete(None, "%_bar_ce")
662 675 nt.assert_in("%_bar_cellm", matches)
663 676 nt.assert_in("%%_bar_cellm", matches)
664 677 s, matches = c.complete(None, "%%_bar_ce")
665 678 nt.assert_not_in("%_bar_cellm", matches)
666 679 nt.assert_in("%%_bar_cellm", matches)
667 680
668 681 def test_magic_completion_order(self):
669 682 ip = get_ipython()
670 683 c = ip.Completer
671 684
672 685 # Test ordering of line and cell magics.
673 686 text, matches = c.complete("timeit")
674 687 nt.assert_equal(matches, ["%timeit", "%%timeit"])
675 688
676 689 def test_magic_completion_shadowing(self):
677 690 ip = get_ipython()
678 691 c = ip.Completer
679 692 c.use_jedi = False
680 693
681 694 # Before importing matplotlib, %matplotlib magic should be the only option.
682 695 text, matches = c.complete("mat")
683 696 nt.assert_equal(matches, ["%matplotlib"])
684 697
685 698 # The newly introduced name should shadow the magic.
686 699 ip.run_cell("matplotlib = 1")
687 700 text, matches = c.complete("mat")
688 701 nt.assert_equal(matches, ["matplotlib"])
689 702
690 703 # After removing matplotlib from namespace, the magic should again be
691 704 # the only option.
692 705 del ip.user_ns["matplotlib"]
693 706 text, matches = c.complete("mat")
694 707 nt.assert_equal(matches, ["%matplotlib"])
695 708
696 709 def test_magic_completion_shadowing_explicit(self):
697 710 """
698 711 If the user try to complete a shadowed magic, and explicit % start should
699 712 still return the completions.
700 713 """
701 714 ip = get_ipython()
702 715 c = ip.Completer
703 716
704 717 # Before importing matplotlib, %matplotlib magic should be the only option.
705 718 text, matches = c.complete("%mat")
706 719 nt.assert_equal(matches, ["%matplotlib"])
707 720
708 721 ip.run_cell("matplotlib = 1")
709 722
710 723 # After removing matplotlib from namespace, the magic should still be
711 724 # the only option.
712 725 text, matches = c.complete("%mat")
713 726 nt.assert_equal(matches, ["%matplotlib"])
714 727
715 728 def test_magic_config(self):
716 729 ip = get_ipython()
717 730 c = ip.Completer
718 731
719 732 s, matches = c.complete(None, "conf")
720 733 nt.assert_in("%config", matches)
721 734 s, matches = c.complete(None, "conf")
722 735 nt.assert_not_in("AliasManager", matches)
723 736 s, matches = c.complete(None, "config ")
724 737 nt.assert_in("AliasManager", matches)
725 738 s, matches = c.complete(None, "%config ")
726 739 nt.assert_in("AliasManager", matches)
727 740 s, matches = c.complete(None, "config Ali")
728 741 nt.assert_list_equal(["AliasManager"], matches)
729 742 s, matches = c.complete(None, "%config Ali")
730 743 nt.assert_list_equal(["AliasManager"], matches)
731 744 s, matches = c.complete(None, "config AliasManager")
732 745 nt.assert_list_equal(["AliasManager"], matches)
733 746 s, matches = c.complete(None, "%config AliasManager")
734 747 nt.assert_list_equal(["AliasManager"], matches)
735 748 s, matches = c.complete(None, "config AliasManager.")
736 749 nt.assert_in("AliasManager.default_aliases", matches)
737 750 s, matches = c.complete(None, "%config AliasManager.")
738 751 nt.assert_in("AliasManager.default_aliases", matches)
739 752 s, matches = c.complete(None, "config AliasManager.de")
740 753 nt.assert_list_equal(["AliasManager.default_aliases"], matches)
741 754 s, matches = c.complete(None, "config AliasManager.de")
742 755 nt.assert_list_equal(["AliasManager.default_aliases"], matches)
743 756
744 757 def test_magic_color(self):
745 758 ip = get_ipython()
746 759 c = ip.Completer
747 760
748 761 s, matches = c.complete(None, "colo")
749 762 nt.assert_in("%colors", matches)
750 763 s, matches = c.complete(None, "colo")
751 764 nt.assert_not_in("NoColor", matches)
752 765 s, matches = c.complete(None, "%colors") # No trailing space
753 766 nt.assert_not_in("NoColor", matches)
754 767 s, matches = c.complete(None, "colors ")
755 768 nt.assert_in("NoColor", matches)
756 769 s, matches = c.complete(None, "%colors ")
757 770 nt.assert_in("NoColor", matches)
758 771 s, matches = c.complete(None, "colors NoCo")
759 772 nt.assert_list_equal(["NoColor"], matches)
760 773 s, matches = c.complete(None, "%colors NoCo")
761 774 nt.assert_list_equal(["NoColor"], matches)
762 775
763 776 def test_match_dict_keys(self):
764 777 """
765 778 Test that match_dict_keys works on a couple of use case does return what
766 779 expected, and does not crash
767 780 """
768 781 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
769 782
770 783 keys = ["foo", b"far"]
771 784 assert match_dict_keys(keys, "b'", delims=delims) == ("'", 2, ["far"])
772 785 assert match_dict_keys(keys, "b'f", delims=delims) == ("'", 2, ["far"])
773 786 assert match_dict_keys(keys, 'b"', delims=delims) == ('"', 2, ["far"])
774 787 assert match_dict_keys(keys, 'b"f', delims=delims) == ('"', 2, ["far"])
775 788
776 789 assert match_dict_keys(keys, "'", delims=delims) == ("'", 1, ["foo"])
777 790 assert match_dict_keys(keys, "'f", delims=delims) == ("'", 1, ["foo"])
778 791 assert match_dict_keys(keys, '"', delims=delims) == ('"', 1, ["foo"])
779 792 assert match_dict_keys(keys, '"f', delims=delims) == ('"', 1, ["foo"])
780 793
781 794 match_dict_keys
782 795
783 796 def test_dict_key_completion_string(self):
784 797 """Test dictionary key completion for string keys"""
785 798 ip = get_ipython()
786 799 complete = ip.Completer.complete
787 800
788 801 ip.user_ns["d"] = {"abc": None}
789 802
790 803 # check completion at different stages
791 804 _, matches = complete(line_buffer="d[")
792 805 nt.assert_in("'abc'", matches)
793 806 nt.assert_not_in("'abc']", matches)
794 807
795 808 _, matches = complete(line_buffer="d['")
796 809 nt.assert_in("abc", matches)
797 810 nt.assert_not_in("abc']", matches)
798 811
799 812 _, matches = complete(line_buffer="d['a")
800 813 nt.assert_in("abc", matches)
801 814 nt.assert_not_in("abc']", matches)
802 815
803 816 # check use of different quoting
804 817 _, matches = complete(line_buffer='d["')
805 818 nt.assert_in("abc", matches)
806 819 nt.assert_not_in('abc"]', matches)
807 820
808 821 _, matches = complete(line_buffer='d["a')
809 822 nt.assert_in("abc", matches)
810 823 nt.assert_not_in('abc"]', matches)
811 824
812 825 # check sensitivity to following context
813 826 _, matches = complete(line_buffer="d[]", cursor_pos=2)
814 827 nt.assert_in("'abc'", matches)
815 828
816 829 _, matches = complete(line_buffer="d['']", cursor_pos=3)
817 830 nt.assert_in("abc", matches)
818 831 nt.assert_not_in("abc'", matches)
819 832 nt.assert_not_in("abc']", matches)
820 833
821 834 # check multiple solutions are correctly returned and that noise is not
822 835 ip.user_ns["d"] = {
823 836 "abc": None,
824 837 "abd": None,
825 838 "bad": None,
826 839 object(): None,
827 840 5: None,
828 841 }
829 842
830 843 _, matches = complete(line_buffer="d['a")
831 844 nt.assert_in("abc", matches)
832 845 nt.assert_in("abd", matches)
833 846 nt.assert_not_in("bad", matches)
834 847 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
835 848
836 849 # check escaping and whitespace
837 850 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
838 851 _, matches = complete(line_buffer="d['a")
839 852 nt.assert_in("a\\nb", matches)
840 853 nt.assert_in("a\\'b", matches)
841 854 nt.assert_in('a"b', matches)
842 855 nt.assert_in("a word", matches)
843 856 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
844 857
845 858 # - can complete on non-initial word of the string
846 859 _, matches = complete(line_buffer="d['a w")
847 860 nt.assert_in("word", matches)
848 861
849 862 # - understands quote escaping
850 863 _, matches = complete(line_buffer="d['a\\'")
851 864 nt.assert_in("b", matches)
852 865
853 866 # - default quoting should work like repr
854 867 _, matches = complete(line_buffer="d[")
855 868 nt.assert_in('"a\'b"', matches)
856 869
857 870 # - when opening quote with ", possible to match with unescaped apostrophe
858 871 _, matches = complete(line_buffer="d[\"a'")
859 872 nt.assert_in("b", matches)
860 873
861 874 # need to not split at delims that readline won't split at
862 875 if "-" not in ip.Completer.splitter.delims:
863 876 ip.user_ns["d"] = {"before-after": None}
864 877 _, matches = complete(line_buffer="d['before-af")
865 878 nt.assert_in("before-after", matches)
866 879
867 880 def test_dict_key_completion_contexts(self):
868 881 """Test expression contexts in which dict key completion occurs"""
869 882 ip = get_ipython()
870 883 complete = ip.Completer.complete
871 884 d = {"abc": None}
872 885 ip.user_ns["d"] = d
873 886
874 887 class C:
875 888 data = d
876 889
877 890 ip.user_ns["C"] = C
878 891 ip.user_ns["get"] = lambda: d
879 892
880 893 def assert_no_completion(**kwargs):
881 894 _, matches = complete(**kwargs)
882 895 nt.assert_not_in("abc", matches)
883 896 nt.assert_not_in("abc'", matches)
884 897 nt.assert_not_in("abc']", matches)
885 898 nt.assert_not_in("'abc'", matches)
886 899 nt.assert_not_in("'abc']", matches)
887 900
888 901 def assert_completion(**kwargs):
889 902 _, matches = complete(**kwargs)
890 903 nt.assert_in("'abc'", matches)
891 904 nt.assert_not_in("'abc']", matches)
892 905
893 906 # no completion after string closed, even if reopened
894 907 assert_no_completion(line_buffer="d['a'")
895 908 assert_no_completion(line_buffer='d["a"')
896 909 assert_no_completion(line_buffer="d['a' + ")
897 910 assert_no_completion(line_buffer="d['a' + '")
898 911
899 912 # completion in non-trivial expressions
900 913 assert_completion(line_buffer="+ d[")
901 914 assert_completion(line_buffer="(d[")
902 915 assert_completion(line_buffer="C.data[")
903 916
904 917 # greedy flag
905 918 def assert_completion(**kwargs):
906 919 _, matches = complete(**kwargs)
907 920 nt.assert_in("get()['abc']", matches)
908 921
909 922 assert_no_completion(line_buffer="get()[")
910 923 with greedy_completion():
911 924 assert_completion(line_buffer="get()[")
912 925 assert_completion(line_buffer="get()['")
913 926 assert_completion(line_buffer="get()['a")
914 927 assert_completion(line_buffer="get()['ab")
915 928 assert_completion(line_buffer="get()['abc")
916 929
917 930 def test_dict_key_completion_bytes(self):
918 931 """Test handling of bytes in dict key completion"""
919 932 ip = get_ipython()
920 933 complete = ip.Completer.complete
921 934
922 935 ip.user_ns["d"] = {"abc": None, b"abd": None}
923 936
924 937 _, matches = complete(line_buffer="d[")
925 938 nt.assert_in("'abc'", matches)
926 939 nt.assert_in("b'abd'", matches)
927 940
928 941 if False: # not currently implemented
929 942 _, matches = complete(line_buffer="d[b")
930 943 nt.assert_in("b'abd'", matches)
931 944 nt.assert_not_in("b'abc'", matches)
932 945
933 946 _, matches = complete(line_buffer="d[b'")
934 947 nt.assert_in("abd", matches)
935 948 nt.assert_not_in("abc", matches)
936 949
937 950 _, matches = complete(line_buffer="d[B'")
938 951 nt.assert_in("abd", matches)
939 952 nt.assert_not_in("abc", matches)
940 953
941 954 _, matches = complete(line_buffer="d['")
942 955 nt.assert_in("abc", matches)
943 956 nt.assert_not_in("abd", matches)
944 957
945 958 def test_dict_key_completion_unicode_py3(self):
946 959 """Test handling of unicode in dict key completion"""
947 960 ip = get_ipython()
948 961 complete = ip.Completer.complete
949 962
950 963 ip.user_ns["d"] = {"a\u05d0": None}
951 964
952 965 # query using escape
953 966 if sys.platform != "win32":
954 967 # Known failure on Windows
955 968 _, matches = complete(line_buffer="d['a\\u05d0")
956 969 nt.assert_in("u05d0", matches) # tokenized after \\
957 970
958 971 # query using character
959 972 _, matches = complete(line_buffer="d['a\u05d0")
960 973 nt.assert_in("a\u05d0", matches)
961 974
962 975 with greedy_completion():
963 976 # query using escape
964 977 _, matches = complete(line_buffer="d['a\\u05d0")
965 978 nt.assert_in("d['a\\u05d0']", matches) # tokenized after \\
966 979
967 980 # query using character
968 981 _, matches = complete(line_buffer="d['a\u05d0")
969 982 nt.assert_in("d['a\u05d0']", matches)
970 983
971 984 @dec.skip_without("numpy")
972 985 def test_struct_array_key_completion(self):
973 986 """Test dict key completion applies to numpy struct arrays"""
974 987 import numpy
975 988
976 989 ip = get_ipython()
977 990 complete = ip.Completer.complete
978 991 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
979 992 _, matches = complete(line_buffer="d['")
980 993 nt.assert_in("hello", matches)
981 994 nt.assert_in("world", matches)
982 995 # complete on the numpy struct itself
983 996 dt = numpy.dtype(
984 997 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
985 998 )
986 999 x = numpy.zeros(2, dtype=dt)
987 1000 ip.user_ns["d"] = x[1]
988 1001 _, matches = complete(line_buffer="d['")
989 1002 nt.assert_in("my_head", matches)
990 1003 nt.assert_in("my_data", matches)
991 1004 # complete on a nested level
992 1005 with greedy_completion():
993 1006 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
994 1007 _, matches = complete(line_buffer="d[1]['my_head']['")
995 1008 nt.assert_true(any(["my_dt" in m for m in matches]))
996 1009 nt.assert_true(any(["my_df" in m for m in matches]))
997 1010
998 1011 @dec.skip_without("pandas")
999 1012 def test_dataframe_key_completion(self):
1000 1013 """Test dict key completion applies to pandas DataFrames"""
1001 1014 import pandas
1002 1015
1003 1016 ip = get_ipython()
1004 1017 complete = ip.Completer.complete
1005 1018 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1006 1019 _, matches = complete(line_buffer="d['")
1007 1020 nt.assert_in("hello", matches)
1008 1021 nt.assert_in("world", matches)
1009 1022
1010 1023 def test_dict_key_completion_invalids(self):
1011 1024 """Smoke test cases dict key completion can't handle"""
1012 1025 ip = get_ipython()
1013 1026 complete = ip.Completer.complete
1014 1027
1015 1028 ip.user_ns["no_getitem"] = None
1016 1029 ip.user_ns["no_keys"] = []
1017 1030 ip.user_ns["cant_call_keys"] = dict
1018 1031 ip.user_ns["empty"] = {}
1019 1032 ip.user_ns["d"] = {"abc": 5}
1020 1033
1021 1034 _, matches = complete(line_buffer="no_getitem['")
1022 1035 _, matches = complete(line_buffer="no_keys['")
1023 1036 _, matches = complete(line_buffer="cant_call_keys['")
1024 1037 _, matches = complete(line_buffer="empty['")
1025 1038 _, matches = complete(line_buffer="name_error['")
1026 1039 _, matches = complete(line_buffer="d['\\") # incomplete escape
1027 1040
1028 1041 def test_object_key_completion(self):
1029 1042 ip = get_ipython()
1030 1043 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1031 1044
1032 1045 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1033 1046 nt.assert_in("qwerty", matches)
1034 1047 nt.assert_in("qwick", matches)
1035 1048
1036 1049 def test_class_key_completion(self):
1037 1050 ip = get_ipython()
1038 1051 NamedInstanceClass("qwerty")
1039 1052 NamedInstanceClass("qwick")
1040 1053 ip.user_ns["named_instance_class"] = NamedInstanceClass
1041 1054
1042 1055 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1043 1056 nt.assert_in("qwerty", matches)
1044 1057 nt.assert_in("qwick", matches)
1045 1058
1046 1059 def test_tryimport(self):
1047 1060 """
1048 1061 Test that try-import don't crash on trailing dot, and import modules before
1049 1062 """
1050 1063 from IPython.core.completerlib import try_import
1051 1064
1052 1065 assert try_import("IPython.")
1053 1066
1054 1067 def test_aimport_module_completer(self):
1055 1068 ip = get_ipython()
1056 1069 _, matches = ip.complete("i", "%aimport i")
1057 1070 nt.assert_in("io", matches)
1058 1071 nt.assert_not_in("int", matches)
1059 1072
1060 1073 def test_nested_import_module_completer(self):
1061 1074 ip = get_ipython()
1062 1075 _, matches = ip.complete(None, "import IPython.co", 17)
1063 1076 nt.assert_in("IPython.core", matches)
1064 1077 nt.assert_not_in("import IPython.core", matches)
1065 1078 nt.assert_not_in("IPython.display", matches)
1066 1079
1067 1080 def test_import_module_completer(self):
1068 1081 ip = get_ipython()
1069 1082 _, matches = ip.complete("i", "import i")
1070 1083 nt.assert_in("io", matches)
1071 1084 nt.assert_not_in("int", matches)
1072 1085
1073 1086 def test_from_module_completer(self):
1074 1087 ip = get_ipython()
1075 1088 _, matches = ip.complete("B", "from io import B", 16)
1076 1089 nt.assert_in("BytesIO", matches)
1077 1090 nt.assert_not_in("BaseException", matches)
1078 1091
1079 1092 def test_snake_case_completion(self):
1080 1093 ip = get_ipython()
1081 1094 ip.Completer.use_jedi = False
1082 1095 ip.user_ns["some_three"] = 3
1083 1096 ip.user_ns["some_four"] = 4
1084 1097 _, matches = ip.complete("s_", "print(s_f")
1085 1098 nt.assert_in("some_three", matches)
1086 1099 nt.assert_in("some_four", matches)
1087 1100
1088 1101 def test_mix_terms(self):
1089 1102 ip = get_ipython()
1090 1103 from textwrap import dedent
1091 1104
1092 1105 ip.Completer.use_jedi = False
1093 1106 ip.ex(
1094 1107 dedent(
1095 1108 """
1096 1109 class Test:
1097 1110 def meth(self, meth_arg1):
1098 1111 print("meth")
1099 1112
1100 1113 def meth_1(self, meth1_arg1, meth1_arg2):
1101 1114 print("meth1")
1102 1115
1103 1116 def meth_2(self, meth2_arg1, meth2_arg2):
1104 1117 print("meth2")
1105 1118 test = Test()
1106 1119 """
1107 1120 )
1108 1121 )
1109 1122 _, matches = ip.complete(None, "test.meth(")
1110 1123 nt.assert_in("meth_arg1=", matches)
1111 1124 nt.assert_not_in("meth2_arg1=", matches)
@@ -1,118 +1,124 b''
1 1 #-----------------------------------------------------------------------------
2 2 # Copyright (C) 2010-2011, IPython Development Team.
3 3 #
4 4 # Distributed under the terms of the Modified BSD License.
5 5 #
6 6 # The full license is in the file COPYING.txt, distributed with this software.
7 7 #-----------------------------------------------------------------------------
8 8
9 9 import argparse
10 import sys
10 11 from nose.tools import assert_equal
11 12
12 13 from IPython.core.magic_arguments import (argument, argument_group, kwds,
13 14 magic_arguments, parse_argstring, real_name)
14 15
15 16
16 17 @magic_arguments()
17 18 @argument('-f', '--foo', help="an argument")
18 19 def magic_foo1(self, args):
19 20 """ A docstring.
20 21 """
21 22 return parse_argstring(magic_foo1, args)
22 23
23 24
24 25 @magic_arguments()
25 26 def magic_foo2(self, args):
26 27 """ A docstring.
27 28 """
28 29 return parse_argstring(magic_foo2, args)
29 30
30 31
31 32 @magic_arguments()
32 33 @argument('-f', '--foo', help="an argument")
33 34 @argument_group('Group')
34 35 @argument('-b', '--bar', help="a grouped argument")
35 36 @argument_group('Second Group')
36 37 @argument('-z', '--baz', help="another grouped argument")
37 38 def magic_foo3(self, args):
38 39 """ A docstring.
39 40 """
40 41 return parse_argstring(magic_foo3, args)
41 42
42 43
43 44 @magic_arguments()
44 45 @kwds(argument_default=argparse.SUPPRESS)
45 46 @argument('-f', '--foo', help="an argument")
46 47 def magic_foo4(self, args):
47 48 """ A docstring.
48 49 """
49 50 return parse_argstring(magic_foo4, args)
50 51
51 52
52 53 @magic_arguments('frobnicate')
53 54 @argument('-f', '--foo', help="an argument")
54 55 def magic_foo5(self, args):
55 56 """ A docstring.
56 57 """
57 58 return parse_argstring(magic_foo5, args)
58 59
59 60
60 61 @magic_arguments()
61 62 @argument('-f', '--foo', help="an argument")
62 63 def magic_magic_foo(self, args):
63 64 """ A docstring.
64 65 """
65 66 return parse_argstring(magic_magic_foo, args)
66 67
67 68
68 69 @magic_arguments()
69 70 @argument('-f', '--foo', help="an argument")
70 71 def foo(self, args):
71 72 """ A docstring.
72 73 """
73 74 return parse_argstring(foo, args)
74 75
75 76
76 77 def test_magic_arguments():
77 assert_equal(magic_foo1.__doc__, '::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
78 # β€œoptional arguments” was replaced with β€œoptions” in argparse help
79 # https://docs.python.org/3/whatsnew/3.10.html#argparse
80 # https://bugs.python.org/issue9694
81 options = "optional arguments" if sys.version_info < (3, 10) else "options"
82
83 assert_equal(magic_foo1.__doc__, f"::\n\n %foo1 [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
78 84 assert_equal(getattr(magic_foo1, 'argcmd_name', None), None)
79 85 assert_equal(real_name(magic_foo1), 'foo1')
80 86 assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None))
81 87 assert hasattr(magic_foo1, 'has_arguments')
82 88
83 89 assert_equal(magic_foo2.__doc__, '::\n\n %foo2\n\n A docstring.\n')
84 90 assert_equal(getattr(magic_foo2, 'argcmd_name', None), None)
85 91 assert_equal(real_name(magic_foo2), 'foo2')
86 92 assert_equal(magic_foo2(None, ''), argparse.Namespace())
87 93 assert hasattr(magic_foo2, 'has_arguments')
88 94
89 assert_equal(magic_foo3.__doc__, '::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n')
95 assert_equal(magic_foo3.__doc__, f"::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n")
90 96 assert_equal(getattr(magic_foo3, 'argcmd_name', None), None)
91 97 assert_equal(real_name(magic_foo3), 'foo3')
92 98 assert_equal(magic_foo3(None, ''),
93 99 argparse.Namespace(bar=None, baz=None, foo=None))
94 100 assert hasattr(magic_foo3, 'has_arguments')
95 101
96 assert_equal(magic_foo4.__doc__, '::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
102 assert_equal(magic_foo4.__doc__, f"::\n\n %foo4 [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
97 103 assert_equal(getattr(magic_foo4, 'argcmd_name', None), None)
98 104 assert_equal(real_name(magic_foo4), 'foo4')
99 105 assert_equal(magic_foo4(None, ''), argparse.Namespace())
100 106 assert hasattr(magic_foo4, 'has_arguments')
101 107
102 assert_equal(magic_foo5.__doc__, '::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
108 assert_equal(magic_foo5.__doc__, f"::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
103 109 assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate')
104 110 assert_equal(real_name(magic_foo5), 'frobnicate')
105 111 assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None))
106 112 assert hasattr(magic_foo5, 'has_arguments')
107 113
108 assert_equal(magic_magic_foo.__doc__, '::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
114 assert_equal(magic_magic_foo.__doc__, f"::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
109 115 assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None)
110 116 assert_equal(real_name(magic_magic_foo), 'magic_foo')
111 117 assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None))
112 118 assert hasattr(magic_magic_foo, 'has_arguments')
113 119
114 assert_equal(foo.__doc__, '::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n')
120 assert_equal(foo.__doc__, f"::\n\n %foo [-f FOO]\n\n A docstring.\n\n{options}:\n -f FOO, --foo FOO an argument\n")
115 121 assert_equal(getattr(foo, 'argcmd_name', None), None)
116 122 assert_equal(real_name(foo), 'foo')
117 123 assert_equal(foo(None, ''), argparse.Namespace(foo=None))
118 124 assert hasattr(foo, 'has_arguments')
@@ -1,447 +1,476 b''
1 1 """Tests for the object inspection functionality.
2 2 """
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from inspect import signature, Signature, Parameter
9 import inspect
9 10 import os
11 import pytest
10 12 import re
11
12 import nose.tools as nt
13 import sys
13 14
14 15 from .. import oinspect
15 16
16 17 from decorator import decorator
17 18
18 19 from IPython.testing.tools import AssertPrints, AssertNotPrints
19 20 from IPython.utils.path import compress_user
20 21
21 22
22 23 #-----------------------------------------------------------------------------
23 24 # Globals and constants
24 25 #-----------------------------------------------------------------------------
25 26
26 27 inspector = None
27 28
28 29 def setup_module():
29 30 global inspector
30 31 inspector = oinspect.Inspector()
31 32
32 33
34 class SourceModuleMainTest:
35 __module__ = "__main__"
36
37
33 38 #-----------------------------------------------------------------------------
34 39 # Local utilities
35 40 #-----------------------------------------------------------------------------
36 41
37 42 # WARNING: since this test checks the line number where a function is
38 43 # defined, if any code is inserted above, the following line will need to be
39 44 # updated. Do NOT insert any whitespace between the next line and the function
40 45 # definition below.
41 THIS_LINE_NUMBER = 41 # Put here the actual number of this line
46 THIS_LINE_NUMBER = 46 # Put here the actual number of this line
47
48
49 def test_find_source_lines():
50 assert oinspect.find_source_lines(test_find_source_lines) == THIS_LINE_NUMBER + 3
51 assert oinspect.find_source_lines(type) is None
52 assert oinspect.find_source_lines(SourceModuleMainTest) is None
53 assert oinspect.find_source_lines(SourceModuleMainTest()) is None
54
42 55
43 from unittest import TestCase
56 def test_getsource():
57 assert oinspect.getsource(type) is None
58 assert oinspect.getsource(SourceModuleMainTest) is None
59 assert oinspect.getsource(SourceModuleMainTest()) is None
44 60
45 class Test(TestCase):
46 61
47 def test_find_source_lines(self):
48 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
49 THIS_LINE_NUMBER+6)
62 def test_inspect_getfile_raises_exception():
63 """Check oinspect.find_file/getsource/find_source_lines expectations"""
64 with pytest.raises(TypeError):
65 inspect.getfile(type)
66 with pytest.raises(OSError if sys.version_info >= (3, 10) else TypeError):
67 inspect.getfile(SourceModuleMainTest)
50 68
51 69
52 70 # A couple of utilities to ensure these tests work the same from a source or a
53 71 # binary install
54 72 def pyfile(fname):
55 73 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
56 74
57 75
58 76 def match_pyfiles(f1, f2):
59 nt.assert_equal(pyfile(f1), pyfile(f2))
77 assert pyfile(f1) == pyfile(f2)
60 78
61 79
62 80 def test_find_file():
63 81 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
82 assert oinspect.find_file(type) is None
83 assert oinspect.find_file(SourceModuleMainTest) is None
84 assert oinspect.find_file(SourceModuleMainTest()) is None
64 85
65 86
66 87 def test_find_file_decorated1():
67 88
68 89 @decorator
69 90 def noop1(f):
70 91 def wrapper(*a, **kw):
71 92 return f(*a, **kw)
72 93 return wrapper
73 94
74 95 @noop1
75 96 def f(x):
76 97 "My docstring"
77
98
78 99 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
79 nt.assert_equal(f.__doc__, "My docstring")
100 assert f.__doc__ == "My docstring"
80 101
81 102
82 103 def test_find_file_decorated2():
83 104
84 105 @decorator
85 106 def noop2(f, *a, **kw):
86 107 return f(*a, **kw)
87 108
88 109 @noop2
89 110 @noop2
90 111 @noop2
91 112 def f(x):
92 113 "My docstring 2"
93
114
94 115 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
95 nt.assert_equal(f.__doc__, "My docstring 2")
96
116 assert f.__doc__ == "My docstring 2"
117
97 118
98 119 def test_find_file_magic():
99 120 run = ip.find_line_magic('run')
100 nt.assert_not_equal(oinspect.find_file(run), None)
121 assert oinspect.find_file(run) is not None
101 122
102 123
103 124 # A few generic objects we can then inspect in the tests below
104 125
105 126 class Call(object):
106 127 """This is the class docstring."""
107 128
108 129 def __init__(self, x, y=1):
109 130 """This is the constructor docstring."""
110 131
111 132 def __call__(self, *a, **kw):
112 133 """This is the call docstring."""
113 134
114 135 def method(self, x, z=2):
115 136 """Some method's docstring"""
116 137
117 138 class HasSignature(object):
118 139 """This is the class docstring."""
119 140 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
120 141
121 142 def __init__(self, *args):
122 143 """This is the init docstring"""
123 144
124 145
125 146 class SimpleClass(object):
126 147 def method(self, x, z=2):
127 148 """Some method's docstring"""
128 149
129 150
130 151 class Awkward(object):
131 152 def __getattr__(self, name):
132 153 raise Exception(name)
133 154
134 155 class NoBoolCall:
135 156 """
136 157 callable with `__bool__` raising should still be inspect-able.
137 158 """
138 159
139 160 def __call__(self):
140 161 """does nothing"""
141 162 pass
142 163
143 164 def __bool__(self):
144 165 """just raise NotImplemented"""
145 166 raise NotImplementedError('Must be implemented')
146 167
147 168
148 169 class SerialLiar(object):
149 170 """Attribute accesses always get another copy of the same class.
150 171
151 172 unittest.mock.call does something similar, but it's not ideal for testing
152 173 as the failure mode is to eat all your RAM. This gives up after 10k levels.
153 174 """
154 175 def __init__(self, max_fibbing_twig, lies_told=0):
155 176 if lies_told > 10000:
156 177 raise RuntimeError('Nose too long, honesty is the best policy')
157 178 self.max_fibbing_twig = max_fibbing_twig
158 179 self.lies_told = lies_told
159 180 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
160 181
161 182 def __getattr__(self, item):
162 183 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
163 184
164 185 #-----------------------------------------------------------------------------
165 186 # Tests
166 187 #-----------------------------------------------------------------------------
167 188
168 189 def test_info():
169 190 "Check that Inspector.info fills out various fields as expected."
170 i = inspector.info(Call, oname='Call')
171 nt.assert_equal(i['type_name'], 'type')
172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
173 nt.assert_equal(i['base_class'], expted_class)
174 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
191 i = inspector.info(Call, oname="Call")
192 assert i["type_name"] == "type"
193 expected_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
194 assert i["base_class"] == expected_class
195 assert re.search(
196 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
197 i["string_form"],
198 )
175 199 fname = __file__
176 200 if fname.endswith(".pyc"):
177 201 fname = fname[:-1]
178 202 # case-insensitive comparison needed on some filesystems
179 203 # e.g. Windows:
180 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
181 nt.assert_equal(i['definition'], None)
182 nt.assert_equal(i['docstring'], Call.__doc__)
183 nt.assert_equal(i['source'], None)
184 nt.assert_true(i['isclass'])
185 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
186 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
204 assert i["file"].lower() == compress_user(fname).lower()
205 assert i["definition"] == None
206 assert i["docstring"] == Call.__doc__
207 assert i["source"] == None
208 assert i["isclass"] is True
209 assert i["init_definition"] == "Call(x, y=1)"
210 assert i["init_docstring"] == Call.__init__.__doc__
187 211
188 212 i = inspector.info(Call, detail_level=1)
189 nt.assert_not_equal(i['source'], None)
190 nt.assert_equal(i['docstring'], None)
213 assert i["source"] is not None
214 assert i["docstring"] == None
191 215
192 216 c = Call(1)
193 217 c.__doc__ = "Modified instance docstring"
194 218 i = inspector.info(c)
195 nt.assert_equal(i['type_name'], 'Call')
196 nt.assert_equal(i['docstring'], "Modified instance docstring")
197 nt.assert_equal(i['class_docstring'], Call.__doc__)
198 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
199 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
219 assert i["type_name"] == "Call"
220 assert i["docstring"] == "Modified instance docstring"
221 assert i["class_docstring"] == Call.__doc__
222 assert i["init_docstring"] == Call.__init__.__doc__
223 assert i["call_docstring"] == Call.__call__.__doc__
224
200 225
201 226 def test_class_signature():
202 info = inspector.info(HasSignature, 'HasSignature')
203 nt.assert_equal(info['init_definition'], "HasSignature(test)")
204 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
227 info = inspector.info(HasSignature, "HasSignature")
228 assert info["init_definition"] == "HasSignature(test)"
229 assert info["init_docstring"] == HasSignature.__init__.__doc__
230
205 231
206 232 def test_info_awkward():
207 233 # Just test that this doesn't throw an error.
208 234 inspector.info(Awkward())
209 235
210 236 def test_bool_raise():
211 237 inspector.info(NoBoolCall())
212 238
213 239 def test_info_serialliar():
214 240 fib_tracker = [0]
215 241 inspector.info(SerialLiar(fib_tracker))
216 242
217 243 # Nested attribute access should be cut off at 100 levels deep to avoid
218 244 # infinite loops: https://github.com/ipython/ipython/issues/9122
219 nt.assert_less(fib_tracker[0], 9000)
245 assert fib_tracker[0] < 9000
220 246
221 247 def support_function_one(x, y=2, *a, **kw):
222 248 """A simple function."""
223 249
224 250 def test_calldef_none():
225 251 # We should ignore __call__ for all of these.
226 252 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
227 253 i = inspector.info(obj)
228 nt.assert_is(i['call_def'], None)
254 assert i["call_def"] is None
255
229 256
230 257 def f_kwarg(pos, *, kwonly):
231 258 pass
232 259
233 260 def test_definition_kwonlyargs():
234 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
235 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
261 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
262 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
263
236 264
237 265 def test_getdoc():
238 266 class A(object):
239 267 """standard docstring"""
240 268 pass
241 269
242 270 class B(object):
243 271 """standard docstring"""
244 272 def getdoc(self):
245 273 return "custom docstring"
246
274
247 275 class C(object):
248 276 """standard docstring"""
249 277 def getdoc(self):
250 278 return None
251
279
252 280 a = A()
253 281 b = B()
254 282 c = C()
255
256 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
257 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
258 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
283
284 assert oinspect.getdoc(a) == "standard docstring"
285 assert oinspect.getdoc(b) == "custom docstring"
286 assert oinspect.getdoc(c) == "standard docstring"
259 287
260 288
261 289 def test_empty_property_has_no_source():
262 290 i = inspector.info(property(), detail_level=1)
263 nt.assert_is(i['source'], None)
291 assert i["source"] is None
264 292
265 293
266 294 def test_property_sources():
267 import posixpath
268 295 # A simple adder whose source and signature stays
269 296 # the same across Python distributions
270 297 def simple_add(a, b):
271 298 "Adds two numbers"
272 299 return a + b
273
300
274 301 class A(object):
275 302 @property
276 303 def foo(self):
277 304 return 'bar'
278 305
279 306 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
280 307
281 dname = property(posixpath.dirname)
282 adder = property(simple_add)
308 dname = property(oinspect.getdoc)
309 adder = property(simple_add)
283 310
284 311 i = inspector.info(A.foo, detail_level=1)
285 nt.assert_in('def foo(self):', i['source'])
286 nt.assert_in('lambda self, v:', i['source'])
312 assert "def foo(self):" in i["source"]
313 assert "lambda self, v:" in i["source"]
287 314
288 315 i = inspector.info(A.dname, detail_level=1)
289 nt.assert_in('def dirname(p)', i['source'])
290
316 assert "def getdoc(obj)" in i["source"]
317
291 318 i = inspector.info(A.adder, detail_level=1)
292 nt.assert_in('def simple_add(a, b)', i['source'])
319 assert "def simple_add(a, b)" in i["source"]
293 320
294 321
295 322 def test_property_docstring_is_in_info_for_detail_level_0():
296 323 class A(object):
297 324 @property
298 325 def foobar(self):
299 326 """This is `foobar` property."""
300 327 pass
301 328
302 ip.user_ns['a_obj'] = A()
303 nt.assert_equal(
304 'This is `foobar` property.',
305 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
329 ip.user_ns["a_obj"] = A()
330 assert (
331 "This is `foobar` property."
332 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
333 )
306 334
307 ip.user_ns['a_cls'] = A
308 nt.assert_equal(
309 'This is `foobar` property.',
310 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
335 ip.user_ns["a_cls"] = A
336 assert (
337 "This is `foobar` property."
338 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
339 )
311 340
312 341
313 342 def test_pdef():
314 343 # See gh-1914
315 344 def foo(): pass
316 345 inspector.pdef(foo, 'foo')
317 346
318 347
319 348 def test_pinfo_nonascii():
320 349 # See gh-1177
321 350 from . import nonascii2
322 351 ip.user_ns['nonascii2'] = nonascii2
323 352 ip._inspect('pinfo', 'nonascii2', detail_level=1)
324 353
325 354 def test_pinfo_type():
326 355 """
327 356 type can fail in various edge case, for example `type.__subclass__()`
328 357 """
329 358 ip._inspect('pinfo', 'type')
330 359
331 360
332 361 def test_pinfo_docstring_no_source():
333 362 """Docstring should be included with detail_level=1 if there is no source"""
334 363 with AssertPrints('Docstring:'):
335 364 ip._inspect('pinfo', 'str.format', detail_level=0)
336 365 with AssertPrints('Docstring:'):
337 366 ip._inspect('pinfo', 'str.format', detail_level=1)
338 367
339 368
340 369 def test_pinfo_no_docstring_if_source():
341 370 """Docstring should not be included with detail_level=1 if source is found"""
342 371 def foo():
343 372 """foo has a docstring"""
344 373
345 374 ip.user_ns['foo'] = foo
346 375
347 376 with AssertPrints('Docstring:'):
348 377 ip._inspect('pinfo', 'foo', detail_level=0)
349 378 with AssertPrints('Source:'):
350 379 ip._inspect('pinfo', 'foo', detail_level=1)
351 380 with AssertNotPrints('Docstring:'):
352 381 ip._inspect('pinfo', 'foo', detail_level=1)
353 382
354 383
355 384 def test_pinfo_docstring_if_detail_and_no_source():
356 385 """ Docstring should be displayed if source info not available """
357 386 obj_def = '''class Foo(object):
358 387 """ This is a docstring for Foo """
359 388 def bar(self):
360 389 """ This is a docstring for Foo.bar """
361 390 pass
362 '''
363
391 '''
392
364 393 ip.run_cell(obj_def)
365 394 ip.run_cell('foo = Foo()')
366
395
367 396 with AssertNotPrints("Source:"):
368 397 with AssertPrints('Docstring:'):
369 398 ip._inspect('pinfo', 'foo', detail_level=0)
370 399 with AssertPrints('Docstring:'):
371 400 ip._inspect('pinfo', 'foo', detail_level=1)
372 401 with AssertPrints('Docstring:'):
373 402 ip._inspect('pinfo', 'foo.bar', detail_level=0)
374 403
375 404 with AssertNotPrints('Docstring:'):
376 405 with AssertPrints('Source:'):
377 406 ip._inspect('pinfo', 'foo.bar', detail_level=1)
378 407
379 408
380 409 def test_pinfo_magic():
381 410 with AssertPrints('Docstring:'):
382 411 ip._inspect('pinfo', 'lsmagic', detail_level=0)
383 412
384 413 with AssertPrints('Source:'):
385 414 ip._inspect('pinfo', 'lsmagic', detail_level=1)
386 415
387 416
388 417 def test_init_colors():
389 418 # ensure colors are not present in signature info
390 419 info = inspector.info(HasSignature)
391 init_def = info['init_definition']
392 nt.assert_not_in('[0m', init_def)
420 init_def = info["init_definition"]
421 assert "[0m" not in init_def
393 422
394 423
395 424 def test_builtin_init():
396 425 info = inspector.info(list)
397 426 init_def = info['init_definition']
398 nt.assert_is_not_none(init_def)
427 assert init_def is not None
399 428
400 429
401 430 def test_render_signature_short():
402 431 def short_fun(a=1): pass
403 432 sig = oinspect._render_signature(
404 433 signature(short_fun),
405 434 short_fun.__name__,
406 435 )
407 nt.assert_equal(sig, 'short_fun(a=1)')
436 assert sig == "short_fun(a=1)"
408 437
409 438
410 439 def test_render_signature_long():
411 440 from typing import Optional
412 441
413 442 def long_function(
414 443 a_really_long_parameter: int,
415 444 and_another_long_one: bool = False,
416 445 let_us_make_sure_this_is_looong: Optional[str] = None,
417 446 ) -> bool: pass
418 447
419 448 sig = oinspect._render_signature(
420 449 signature(long_function),
421 450 long_function.__name__,
422 451 )
423 nt.assert_in(sig, [
452 assert sig in [
424 453 # Python >=3.9
425 454 '''\
426 455 long_function(
427 456 a_really_long_parameter: int,
428 457 and_another_long_one: bool = False,
429 458 let_us_make_sure_this_is_looong: Optional[str] = None,
430 459 ) -> bool\
431 460 ''',
432 461 # Python >=3.7
433 462 '''\
434 463 long_function(
435 464 a_really_long_parameter: int,
436 465 and_another_long_one: bool = False,
437 466 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
438 467 ) -> bool\
439 468 ''', # Python <=3.6
440 469 '''\
441 470 long_function(
442 471 a_really_long_parameter:int,
443 472 and_another_long_one:bool=False,
444 473 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
445 474 ) -> bool\
446 475 ''',
447 ])
476 ] No newline at end of file
@@ -1,472 +1,476 b''
1 1 # coding: utf-8
2 2 """Tests for IPython.lib.pretty."""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from collections import Counter, defaultdict, deque, OrderedDict
9 9 import os
10 10 import types
11 11 import string
12 import sys
12 13 import unittest
13 14
14 15 import nose.tools as nt
15 16
16 17 from IPython.lib import pretty
17 18 from IPython.testing.decorators import skip_without
18 19
19 20 from io import StringIO
20 21
21 22
22 23 class MyList(object):
23 24 def __init__(self, content):
24 25 self.content = content
25 26 def _repr_pretty_(self, p, cycle):
26 27 if cycle:
27 28 p.text("MyList(...)")
28 29 else:
29 30 with p.group(3, "MyList(", ")"):
30 31 for (i, child) in enumerate(self.content):
31 32 if i:
32 33 p.text(",")
33 34 p.breakable()
34 35 else:
35 36 p.breakable("")
36 37 p.pretty(child)
37 38
38 39
39 40 class MyDict(dict):
40 41 def _repr_pretty_(self, p, cycle):
41 42 p.text("MyDict(...)")
42 43
43 44 class MyObj(object):
44 45 def somemethod(self):
45 46 pass
46 47
47 48
48 49 class Dummy1(object):
49 50 def _repr_pretty_(self, p, cycle):
50 51 p.text("Dummy1(...)")
51 52
52 53 class Dummy2(Dummy1):
53 54 _repr_pretty_ = None
54 55
55 56 class NoModule(object):
56 57 pass
57 58
58 59 NoModule.__module__ = None
59 60
60 61 class Breaking(object):
61 62 def _repr_pretty_(self, p, cycle):
62 63 with p.group(4,"TG: ",":"):
63 64 p.text("Breaking(")
64 65 p.break_()
65 66 p.text(")")
66 67
67 68 class BreakingRepr(object):
68 69 def __repr__(self):
69 70 return "Breaking(\n)"
70 71
71 72 class BadRepr(object):
72 73
73 74 def __repr__(self):
74 75 return 1/0
75 76
76 77
77 78 def test_indentation():
78 79 """Test correct indentation in groups"""
79 80 count = 40
80 81 gotoutput = pretty.pretty(MyList(range(count)))
81 82 expectedoutput = "MyList(\n" + ",\n".join(" %d" % i for i in range(count)) + ")"
82 83
83 84 nt.assert_equal(gotoutput, expectedoutput)
84 85
85 86
86 87 def test_dispatch():
87 88 """
88 89 Test correct dispatching: The _repr_pretty_ method for MyDict
89 90 must be found before the registered printer for dict.
90 91 """
91 92 gotoutput = pretty.pretty(MyDict())
92 93 expectedoutput = "MyDict(...)"
93 94
94 95 nt.assert_equal(gotoutput, expectedoutput)
95 96
96 97
97 98 def test_callability_checking():
98 99 """
99 100 Test that the _repr_pretty_ method is tested for callability and skipped if
100 101 not.
101 102 """
102 103 gotoutput = pretty.pretty(Dummy2())
103 104 expectedoutput = "Dummy1(...)"
104 105
105 106 nt.assert_equal(gotoutput, expectedoutput)
106 107
107 108
108 109 def test_sets():
109 110 """
110 111 Test that set and frozenset use Python 3 formatting.
111 112 """
112 113 objects = [set(), frozenset(), set([1]), frozenset([1]), set([1, 2]),
113 114 frozenset([1, 2]), set([-1, -2, -3])]
114 115 expected = ['set()', 'frozenset()', '{1}', 'frozenset({1})', '{1, 2}',
115 116 'frozenset({1, 2})', '{-3, -2, -1}']
116 117 for obj, expected_output in zip(objects, expected):
117 118 got_output = pretty.pretty(obj)
118 119 yield nt.assert_equal, got_output, expected_output
119 120
120 121
121 @skip_without('xxlimited')
122 @skip_without("xxlimited" if sys.version_info < (3, 10) else "xxlimited_35")
122 123 def test_pprint_heap_allocated_type():
123 124 """
124 125 Test that pprint works for heap allocated types.
125 126 """
126 import xxlimited
127 if sys.version_info < (3, 10):
128 import xxlimited
129 else:
130 import xxlimited_35 as xxlimited
127 131 output = pretty.pretty(xxlimited.Null)
128 132 nt.assert_equal(output, 'xxlimited.Null')
129 133
130 134 def test_pprint_nomod():
131 135 """
132 136 Test that pprint works for classes with no __module__.
133 137 """
134 138 output = pretty.pretty(NoModule)
135 139 nt.assert_equal(output, 'NoModule')
136 140
137 141 def test_pprint_break():
138 142 """
139 143 Test that p.break_ produces expected output
140 144 """
141 145 output = pretty.pretty(Breaking())
142 146 expected = "TG: Breaking(\n ):"
143 147 nt.assert_equal(output, expected)
144 148
145 149 def test_pprint_break_repr():
146 150 """
147 151 Test that p.break_ is used in repr
148 152 """
149 153 output = pretty.pretty([[BreakingRepr()]])
150 154 expected = "[[Breaking(\n )]]"
151 155 nt.assert_equal(output, expected)
152 156
153 157 output = pretty.pretty([[BreakingRepr()]*2])
154 158 expected = "[[Breaking(\n ),\n Breaking(\n )]]"
155 159 nt.assert_equal(output, expected)
156 160
157 161 def test_bad_repr():
158 162 """Don't catch bad repr errors"""
159 163 with nt.assert_raises(ZeroDivisionError):
160 164 pretty.pretty(BadRepr())
161 165
162 166 class BadException(Exception):
163 167 def __str__(self):
164 168 return -1
165 169
166 170 class ReallyBadRepr(object):
167 171 __module__ = 1
168 172 @property
169 173 def __class__(self):
170 174 raise ValueError("I am horrible")
171 175
172 176 def __repr__(self):
173 177 raise BadException()
174 178
175 179 def test_really_bad_repr():
176 180 with nt.assert_raises(BadException):
177 181 pretty.pretty(ReallyBadRepr())
178 182
179 183
180 184 class SA(object):
181 185 pass
182 186
183 187 class SB(SA):
184 188 pass
185 189
186 190 class TestsPretty(unittest.TestCase):
187 191
188 192 def test_super_repr(self):
189 193 # "<super: module_name.SA, None>"
190 194 output = pretty.pretty(super(SA))
191 195 self.assertRegex(output, r"<super: \S+.SA, None>")
192 196
193 197 # "<super: module_name.SA, <module_name.SB at 0x...>>"
194 198 sb = SB()
195 199 output = pretty.pretty(super(SA, sb))
196 200 self.assertRegex(output, r"<super: \S+.SA,\s+<\S+.SB at 0x\S+>>")
197 201
198 202
199 203 def test_long_list(self):
200 204 lis = list(range(10000))
201 205 p = pretty.pretty(lis)
202 206 last2 = p.rsplit('\n', 2)[-2:]
203 207 self.assertEqual(last2, [' 999,', ' ...]'])
204 208
205 209 def test_long_set(self):
206 210 s = set(range(10000))
207 211 p = pretty.pretty(s)
208 212 last2 = p.rsplit('\n', 2)[-2:]
209 213 self.assertEqual(last2, [' 999,', ' ...}'])
210 214
211 215 def test_long_tuple(self):
212 216 tup = tuple(range(10000))
213 217 p = pretty.pretty(tup)
214 218 last2 = p.rsplit('\n', 2)[-2:]
215 219 self.assertEqual(last2, [' 999,', ' ...)'])
216 220
217 221 def test_long_dict(self):
218 222 d = { n:n for n in range(10000) }
219 223 p = pretty.pretty(d)
220 224 last2 = p.rsplit('\n', 2)[-2:]
221 225 self.assertEqual(last2, [' 999: 999,', ' ...}'])
222 226
223 227 def test_unbound_method(self):
224 228 output = pretty.pretty(MyObj.somemethod)
225 229 self.assertIn('MyObj.somemethod', output)
226 230
227 231
228 232 class MetaClass(type):
229 233 def __new__(cls, name):
230 234 return type.__new__(cls, name, (object,), {'name': name})
231 235
232 236 def __repr__(self):
233 237 return "[CUSTOM REPR FOR CLASS %s]" % self.name
234 238
235 239
236 240 ClassWithMeta = MetaClass('ClassWithMeta')
237 241
238 242
239 243 def test_metaclass_repr():
240 244 output = pretty.pretty(ClassWithMeta)
241 245 nt.assert_equal(output, "[CUSTOM REPR FOR CLASS ClassWithMeta]")
242 246
243 247
244 248 def test_unicode_repr():
245 249 u = u"üniçodé"
246 250 ustr = u
247 251
248 252 class C(object):
249 253 def __repr__(self):
250 254 return ustr
251 255
252 256 c = C()
253 257 p = pretty.pretty(c)
254 258 nt.assert_equal(p, u)
255 259 p = pretty.pretty([c])
256 260 nt.assert_equal(p, u'[%s]' % u)
257 261
258 262
259 263 def test_basic_class():
260 264 def type_pprint_wrapper(obj, p, cycle):
261 265 if obj is MyObj:
262 266 type_pprint_wrapper.called = True
263 267 return pretty._type_pprint(obj, p, cycle)
264 268 type_pprint_wrapper.called = False
265 269
266 270 stream = StringIO()
267 271 printer = pretty.RepresentationPrinter(stream)
268 272 printer.type_pprinters[type] = type_pprint_wrapper
269 273 printer.pretty(MyObj)
270 274 printer.flush()
271 275 output = stream.getvalue()
272 276
273 277 nt.assert_equal(output, '%s.MyObj' % __name__)
274 278 nt.assert_true(type_pprint_wrapper.called)
275 279
276 280
277 281 def test_collections_defaultdict():
278 282 # Create defaultdicts with cycles
279 283 a = defaultdict()
280 284 a.default_factory = a
281 285 b = defaultdict(list)
282 286 b['key'] = b
283 287
284 288 # Dictionary order cannot be relied on, test against single keys.
285 289 cases = [
286 290 (defaultdict(list), 'defaultdict(list, {})'),
287 291 (defaultdict(list, {'key': '-' * 50}),
288 292 "defaultdict(list,\n"
289 293 " {'key': '--------------------------------------------------'})"),
290 294 (a, 'defaultdict(defaultdict(...), {})'),
291 295 (b, "defaultdict(list, {'key': defaultdict(...)})"),
292 296 ]
293 297 for obj, expected in cases:
294 298 nt.assert_equal(pretty.pretty(obj), expected)
295 299
296 300
297 301 def test_collections_ordereddict():
298 302 # Create OrderedDict with cycle
299 303 a = OrderedDict()
300 304 a['key'] = a
301 305
302 306 cases = [
303 307 (OrderedDict(), 'OrderedDict()'),
304 308 (OrderedDict((i, i) for i in range(1000, 1010)),
305 309 'OrderedDict([(1000, 1000),\n'
306 310 ' (1001, 1001),\n'
307 311 ' (1002, 1002),\n'
308 312 ' (1003, 1003),\n'
309 313 ' (1004, 1004),\n'
310 314 ' (1005, 1005),\n'
311 315 ' (1006, 1006),\n'
312 316 ' (1007, 1007),\n'
313 317 ' (1008, 1008),\n'
314 318 ' (1009, 1009)])'),
315 319 (a, "OrderedDict([('key', OrderedDict(...))])"),
316 320 ]
317 321 for obj, expected in cases:
318 322 nt.assert_equal(pretty.pretty(obj), expected)
319 323
320 324
321 325 def test_collections_deque():
322 326 # Create deque with cycle
323 327 a = deque()
324 328 a.append(a)
325 329
326 330 cases = [
327 331 (deque(), 'deque([])'),
328 332 (deque(i for i in range(1000, 1020)),
329 333 'deque([1000,\n'
330 334 ' 1001,\n'
331 335 ' 1002,\n'
332 336 ' 1003,\n'
333 337 ' 1004,\n'
334 338 ' 1005,\n'
335 339 ' 1006,\n'
336 340 ' 1007,\n'
337 341 ' 1008,\n'
338 342 ' 1009,\n'
339 343 ' 1010,\n'
340 344 ' 1011,\n'
341 345 ' 1012,\n'
342 346 ' 1013,\n'
343 347 ' 1014,\n'
344 348 ' 1015,\n'
345 349 ' 1016,\n'
346 350 ' 1017,\n'
347 351 ' 1018,\n'
348 352 ' 1019])'),
349 353 (a, 'deque([deque(...)])'),
350 354 ]
351 355 for obj, expected in cases:
352 356 nt.assert_equal(pretty.pretty(obj), expected)
353 357
354 358 def test_collections_counter():
355 359 class MyCounter(Counter):
356 360 pass
357 361 cases = [
358 362 (Counter(), 'Counter()'),
359 363 (Counter(a=1), "Counter({'a': 1})"),
360 364 (MyCounter(a=1), "MyCounter({'a': 1})"),
361 365 ]
362 366 for obj, expected in cases:
363 367 nt.assert_equal(pretty.pretty(obj), expected)
364 368
365 369 def test_mappingproxy():
366 370 MP = types.MappingProxyType
367 371 underlying_dict = {}
368 372 mp_recursive = MP(underlying_dict)
369 373 underlying_dict[2] = mp_recursive
370 374 underlying_dict[3] = underlying_dict
371 375
372 376 cases = [
373 377 (MP({}), "mappingproxy({})"),
374 378 (MP({None: MP({})}), "mappingproxy({None: mappingproxy({})})"),
375 379 (MP({k: k.upper() for k in string.ascii_lowercase}),
376 380 "mappingproxy({'a': 'A',\n"
377 381 " 'b': 'B',\n"
378 382 " 'c': 'C',\n"
379 383 " 'd': 'D',\n"
380 384 " 'e': 'E',\n"
381 385 " 'f': 'F',\n"
382 386 " 'g': 'G',\n"
383 387 " 'h': 'H',\n"
384 388 " 'i': 'I',\n"
385 389 " 'j': 'J',\n"
386 390 " 'k': 'K',\n"
387 391 " 'l': 'L',\n"
388 392 " 'm': 'M',\n"
389 393 " 'n': 'N',\n"
390 394 " 'o': 'O',\n"
391 395 " 'p': 'P',\n"
392 396 " 'q': 'Q',\n"
393 397 " 'r': 'R',\n"
394 398 " 's': 'S',\n"
395 399 " 't': 'T',\n"
396 400 " 'u': 'U',\n"
397 401 " 'v': 'V',\n"
398 402 " 'w': 'W',\n"
399 403 " 'x': 'X',\n"
400 404 " 'y': 'Y',\n"
401 405 " 'z': 'Z'})"),
402 406 (mp_recursive, "mappingproxy({2: {...}, 3: {2: {...}, 3: {...}}})"),
403 407 (underlying_dict,
404 408 "{2: mappingproxy({2: {...}, 3: {...}}), 3: {...}}"),
405 409 ]
406 410 for obj, expected in cases:
407 411 nt.assert_equal(pretty.pretty(obj), expected)
408 412
409 413
410 414 def test_simplenamespace():
411 415 SN = types.SimpleNamespace
412 416
413 417 sn_recursive = SN()
414 418 sn_recursive.first = sn_recursive
415 419 sn_recursive.second = sn_recursive
416 420 cases = [
417 421 (SN(), "namespace()"),
418 422 (SN(x=SN()), "namespace(x=namespace())"),
419 423 (SN(a_long_name=[SN(s=string.ascii_lowercase)]*3, a_short_name=None),
420 424 "namespace(a_long_name=[namespace(s='abcdefghijklmnopqrstuvwxyz'),\n"
421 425 " namespace(s='abcdefghijklmnopqrstuvwxyz'),\n"
422 426 " namespace(s='abcdefghijklmnopqrstuvwxyz')],\n"
423 427 " a_short_name=None)"),
424 428 (sn_recursive, "namespace(first=namespace(...), second=namespace(...))"),
425 429 ]
426 430 for obj, expected in cases:
427 431 nt.assert_equal(pretty.pretty(obj), expected)
428 432
429 433
430 434 def test_pretty_environ():
431 435 dict_repr = pretty.pretty(dict(os.environ))
432 436 # reindent to align with 'environ' prefix
433 437 dict_indented = dict_repr.replace('\n', '\n' + (' ' * len('environ')))
434 438 env_repr = pretty.pretty(os.environ)
435 439 nt.assert_equal(env_repr, 'environ' + dict_indented)
436 440
437 441
438 442 def test_function_pretty():
439 443 "Test pretty print of function"
440 444 # posixpath is a pure python module, its interface is consistent
441 445 # across Python distributions
442 446 import posixpath
443 447 nt.assert_equal(pretty.pretty(posixpath.join), '<function posixpath.join(a, *p)>')
444 448
445 449 # custom function
446 450 def meaning_of_life(question=None):
447 451 if question:
448 452 return 42
449 453 return "Don't panic"
450 454
451 455 nt.assert_in('meaning_of_life(question=None)', pretty.pretty(meaning_of_life))
452 456
453 457
454 458 class OrderedCounter(Counter, OrderedDict):
455 459 'Counter that remembers the order elements are first encountered'
456 460
457 461 def __repr__(self):
458 462 return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
459 463
460 464 def __reduce__(self):
461 465 return self.__class__, (OrderedDict(self),)
462 466
463 467 class MySet(set): # Override repr of a basic type
464 468 def __repr__(self):
465 469 return 'mine'
466 470
467 471 def test_custom_repr():
468 472 """A custom repr should override a pretty printer for a parent type"""
469 473 oc = OrderedCounter("abracadabra")
470 474 nt.assert_in("OrderedCounter(OrderedDict", pretty.pretty(oc))
471 475
472 476 nt.assert_equal(pretty.pretty(MySet()), 'mine')
General Comments 0
You need to be logged in to leave comments. Login now