| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- """
- Simple utility for splitting user input. This is used by both inputsplitter and
- prefilter.
- """
- # -----------------------------------------------------------------------------
- # Imports
- # -----------------------------------------------------------------------------
- import re
- import sys
- import warnings
- from IPython.core.oinspect import OInfo
- # -----------------------------------------------------------------------------
- # Main function
- # -----------------------------------------------------------------------------
- # RegExp for splitting line contents into pre-char//first word-method//rest.
- # For clarity, each group in on one line.
- # WARNING: update the regexp if the escapes in interactiveshell are changed, as
- # they are hardwired in.
- # Although it's not solely driven by the regex, note that:
- # ,;/% only trigger if they are the first character on the line
- # ! and !! trigger if they are first char(s) *or* follow an indent
- # ? triggers as first or last char.
- line_split = re.compile(
- r"""
- ^(\s*) # any leading space
- ([,;/%]|!!?|\?\??)? # escape character or characters
- \s*(%{0,2}[\w\.\*]*) # function/method, possibly with leading %
- # to correctly treat things like '?%magic'
- (.*?$|$) # rest of line
- """,
- re.VERBOSE,
- )
- def split_user_input(
- line: str, pattern: re.Pattern[str] | None = None
- ) -> tuple[str, str, str, str]:
- """Split user input into initial whitespace, escape character, function part
- and the rest.
- """
- assert isinstance(line, str)
- if pattern is None:
- pattern = line_split
- match = pattern.match(line)
- if not match:
- # print("match failed for line '%s'" % line)
- try:
- ifun, the_rest = line.split(None, 1)
- except ValueError:
- # print("split failed for line '%s'" % line)
- ifun, the_rest = line, ""
- pre = re.match(r"^(\s*)(.*)", line).groups()[0]
- esc = ""
- else:
- pre, esc, ifun, the_rest = match.groups()
- # print('line:<%s>' % line) # dbg
- # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
- return pre, esc or "", ifun.strip(), the_rest
- class LineInfo:
- """A single line of input and associated info.
- Includes the following as properties:
- line
- The original, raw line
- continue_prompt
- Is this line a continuation in a sequence of multiline input?
- pre
- Any leading whitespace.
- esc
- The escape character(s) in pre or the empty string if there isn't one.
- Note that '!!' and '??' are possible values for esc. Otherwise it will
- always be a single character.
- ifun
- The 'function part', which is basically the maximal initial sequence
- of valid python identifiers and the '.' character. This is what is
- checked for alias and magic transformations, used for auto-calling,
- etc. In contrast to Python identifiers, it may start with "%" and contain
- "*".
- the_rest
- Everything else on the line.
- raw_the_rest
- the_rest without whitespace stripped.
- """
- line: str
- continue_prompt: bool
- pre: str
- esc: str
- ifun: str
- raw_the_rest: str
- the_rest: str
- pre_char: str
- pre_whitespace: str
- def __init__(self, line: str, continue_prompt: bool = False) -> None:
- assert isinstance(line, str)
- self.line = line
- self.continue_prompt = continue_prompt
- self.pre, self.esc, self.ifun, self.raw_the_rest = split_user_input(line)
- self.the_rest = self.raw_the_rest.lstrip()
- self.pre_char = self.pre.strip()
- if self.pre_char:
- self.pre_whitespace = "" # No whitespace allowed before esc chars
- else:
- self.pre_whitespace = self.pre
- def ofind(self, ip) -> OInfo:
- """Do a full, attribute-walking lookup of the ifun in the various
- namespaces for the given IPython InteractiveShell instance.
- Return a dict with keys: {found, obj, ospace, ismagic}
- Note: can cause state changes because of calling getattr, but should
- only be run if autocall is on and if the line hasn't matched any
- other, less dangerous handlers.
- Does cache the results of the call, so can be called multiple times
- without worrying about *further* damaging state.
- .. deprecated:: 9.8
- Use ``shell._ofind(line_info.ifun)`` directly instead.
- """
- warnings.warn(
- "LineInfo.ofind() is deprecated since IPython 9.9. "
- "Use shell._ofind(line_info.ifun) directly instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return ip._ofind(self.ifun)
- def __str__(self) -> str:
- return "LineInfo [%s|%s|%s|%s]" % (self.pre, self.esc, self.ifun, self.the_rest)
- def __repr__(self) -> str:
- return "<" + str(self) + ">"
|