""" 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) + ">"