##// END OF EJS Templates
Support square bracket indexing with literal with pinfo/?/pinfo2/??...
Matthias Bussonnier -
Show More
@@ -429,9 +429,11 b' class EscapedCommand(TokenTransformBase):'
429
429
430 return lines_before + [new_line] + lines_after
430 return lines_before + [new_line] + lines_after
431
431
432 _help_end_re = re.compile(r"""(%{0,2}
432
433 _help_end_re = re.compile(
434 r"""(%{0,2}
433 (?!\d)[\w*]+ # Variable name
435 (?!\d)[\w*]+ # Variable name
434 (\.(?!\d)[\w*]+)* # .etc.etc
436 (\.(?!\d)[\w*]+|\[[0-9]+\])* # .etc.etc or [0], we only support literal integers.
435 )
437 )
436 (\?\??)$ # ? or ??
438 (\?\??)$ # ? or ??
437 """,
439 """,
@@ -464,10 +466,11 b' class HelpEnd(TokenTransformBase):'
464 def transform(self, lines):
466 def transform(self, lines):
465 """Transform a help command found by the ``find()`` classmethod.
467 """Transform a help command found by the ``find()`` classmethod.
466 """
468 """
467 piece = ''.join(lines[self.start_line:self.q_line+1])
469
468 indent, content = piece[:self.start_col], piece[self.start_col:]
470 piece = "".join(lines[self.start_line : self.q_line + 1])
469 lines_before = lines[:self.start_line]
471 indent, content = piece[: self.start_col], piece[self.start_col :]
470 lines_after = lines[self.q_line + 1:]
472 lines_before = lines[: self.start_line]
473 lines_after = lines[self.q_line + 1 :]
471
474
472 m = _help_end_re.search(content)
475 m = _help_end_re.search(content)
473 if not m:
476 if not m:
@@ -543,8 +546,13 b' def has_sunken_brackets(tokens: List[tokenize.TokenInfo]):'
543
546
544 def show_linewise_tokens(s: str):
547 def show_linewise_tokens(s: str):
545 """For investigation and debugging"""
548 """For investigation and debugging"""
546 if not s.endswith('\n'):
549 warnings.warn(
547 s += '\n'
550 "show_linewise_tokens is deprecated since IPython 8.6",
551 DeprecationWarning,
552 stacklevel=2,
553 )
554 if not s.endswith("\n"):
555 s += "\n"
548 lines = s.splitlines(keepends=True)
556 lines = s.splitlines(keepends=True)
549 for line in make_tokens_by_line(lines):
557 for line in make_tokens_by_line(lines):
550 print("Line -------")
558 print("Line -------")
@@ -213,14 +213,17 b' class ExecutionInfo(object):'
213 raw_cell = (
213 raw_cell = (
214 (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell
214 (self.raw_cell[:50] + "..") if len(self.raw_cell) > 50 else self.raw_cell
215 )
215 )
216 return '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>' % (
216 return (
217 name,
217 '<%s object at %x, raw_cell="%s" store_history=%s silent=%s shell_futures=%s cell_id=%s>'
218 id(self),
218 % (
219 raw_cell,
219 name,
220 self.store_history,
220 id(self),
221 self.silent,
221 raw_cell,
222 self.shell_futures,
222 self.store_history,
223 self.cell_id,
223 self.silent,
224 self.shell_futures,
225 self.cell_id,
226 )
224 )
227 )
225
228
226
229
@@ -1537,10 +1540,33 b' class InteractiveShell(SingletonConfigurable):'
1537 Has special code to detect magic functions.
1540 Has special code to detect magic functions.
1538 """
1541 """
1539 oname = oname.strip()
1542 oname = oname.strip()
1540 if not oname.startswith(ESC_MAGIC) and \
1543 raw_parts = oname.split(".")
1541 not oname.startswith(ESC_MAGIC2) and \
1544 parts = []
1542 not all(a.isidentifier() for a in oname.split(".")):
1545 parts_ok = True
1543 return {'found': False}
1546 for p in raw_parts:
1547 if p.endswith("]"):
1548 var, *indices = p.split("[")
1549 if not var.isidentifier():
1550 parts_ok = False
1551 break
1552 parts.append(var)
1553 for ind in indices:
1554 if ind[-1] != "]" and not ind[:-1].isnumeric():
1555 parts_ok = False
1556 break
1557 parts.append(ind[:-1])
1558 continue
1559
1560 if not p.isidentifier():
1561 parts_ok = False
1562 parts.append(p)
1563
1564 if (
1565 not oname.startswith(ESC_MAGIC)
1566 and not oname.startswith(ESC_MAGIC2)
1567 and not parts_ok
1568 ):
1569 return {"found": False}
1544
1570
1545 if namespaces is None:
1571 if namespaces is None:
1546 # Namespaces to search in:
1572 # Namespaces to search in:
@@ -1562,7 +1588,7 b' class InteractiveShell(SingletonConfigurable):'
1562 # Look for the given name by splitting it in parts. If the head is
1588 # Look for the given name by splitting it in parts. If the head is
1563 # found, then we look for all the remaining parts as members, and only
1589 # found, then we look for all the remaining parts as members, and only
1564 # declare success if we can find them all.
1590 # declare success if we can find them all.
1565 oname_parts = oname.split('.')
1591 oname_parts = parts
1566 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1592 oname_head, oname_rest = oname_parts[0],oname_parts[1:]
1567 for nsname,ns in namespaces:
1593 for nsname,ns in namespaces:
1568 try:
1594 try:
@@ -1579,7 +1605,10 b' class InteractiveShell(SingletonConfigurable):'
1579 if idx == len(oname_rest) - 1:
1605 if idx == len(oname_rest) - 1:
1580 obj = self._getattr_property(obj, part)
1606 obj = self._getattr_property(obj, part)
1581 else:
1607 else:
1582 obj = getattr(obj, part)
1608 if part.isnumeric():
1609 obj = obj[int(part)]
1610 else:
1611 obj = getattr(obj, part)
1583 except:
1612 except:
1584 # Blanket except b/c some badly implemented objects
1613 # Blanket except b/c some badly implemented objects
1585 # allow __getattr__ to raise exceptions other than
1614 # allow __getattr__ to raise exceptions other than
@@ -1643,7 +1672,10 b' class InteractiveShell(SingletonConfigurable):'
1643 #
1672 #
1644 # The universal alternative is to traverse the mro manually
1673 # The universal alternative is to traverse the mro manually
1645 # searching for attrname in class dicts.
1674 # searching for attrname in class dicts.
1646 attr = getattr(type(obj), attrname)
1675 if attrname.isnumeric():
1676 return obj[int(attrname)]
1677 else:
1678 attr = getattr(type(obj), attrname)
1647 except AttributeError:
1679 except AttributeError:
1648 pass
1680 pass
1649 else:
1681 else:
@@ -5,6 +5,7 b''
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from contextlib import contextmanager
8 from inspect import signature, Signature, Parameter
9 from inspect import signature, Signature, Parameter
9 import inspect
10 import inspect
10 import os
11 import os
@@ -43,7 +44,7 b' class SourceModuleMainTest:'
43 # defined, if any code is inserted above, the following line will need to be
44 # defined, if any code is inserted above, the following line will need to be
44 # updated. Do NOT insert any whitespace between the next line and the function
45 # updated. Do NOT insert any whitespace between the next line and the function
45 # definition below.
46 # definition below.
46 THIS_LINE_NUMBER = 46 # Put here the actual number of this line
47 THIS_LINE_NUMBER = 47 # Put here the actual number of this line
47
48
48
49
49 def test_find_source_lines():
50 def test_find_source_lines():
@@ -345,6 +346,56 b' def test_pdef():'
345 inspector.pdef(foo, 'foo')
346 inspector.pdef(foo, 'foo')
346
347
347
348
349 @contextmanager
350 def cleanup_user_ns(**kwargs):
351 """
352 On exit delete all the keys that were not in user_ns before entering.
353
354 It does not restore old values !
355
356 Parameters
357 ----------
358
359 **kwargs
360 used to update ip.user_ns
361
362 """
363 try:
364 known = set(ip.user_ns.keys())
365 ip.user_ns.update(kwargs)
366 yield
367 finally:
368 added = set(ip.user_ns.keys()) - known
369 for k in added:
370 del ip.user_ns[k]
371
372
373 def test_pinfo_getindex():
374 def dummy():
375 """
376 MARKER
377 """
378
379 container = [dummy]
380 with cleanup_user_ns(container=container):
381 with AssertPrints("MARKER"):
382 ip._inspect("pinfo", "container[0]", detail_level=0)
383 assert "container" not in ip.user_ns.keys()
384
385
386 def test_qmark_getindex():
387 def dummy():
388 """
389 MARKER 2
390 """
391
392 container = [dummy]
393 with cleanup_user_ns(container=container):
394 with AssertPrints("MARKER 2"):
395 ip.run_cell("container[0]?")
396 assert "container" not in ip.user_ns.keys()
397
398
348 def test_pinfo_nonascii():
399 def test_pinfo_nonascii():
349 # See gh-1177
400 # See gh-1177
350 from . import nonascii2
401 from . import nonascii2
@@ -782,7 +782,7 b' def _init_checker_class() -> Type["IPDoctestOutputChecker"]:'
782 precision = 0 if fraction is None else len(fraction)
782 precision = 0 if fraction is None else len(fraction)
783 if exponent is not None:
783 if exponent is not None:
784 precision -= int(exponent)
784 precision -= int(exponent)
785 if float(w.group()) == approx(float(g.group()), abs=10 ** -precision):
785 if float(w.group()) == approx(float(g.group()), abs=10**-precision):
786 # They're close enough. Replace the text we actually
786 # They're close enough. Replace the text we actually
787 # got with the text we want, so that it will match when we
787 # got with the text we want, so that it will match when we
788 # check the string literally.
788 # check the string literally.
General Comments 0
You need to be logged in to leave comments. Login now