1"""
2Simple utility for splitting user input. This is used by both inputsplitter and
3prefilter.
4"""
5
6# -----------------------------------------------------------------------------
7# Imports
8# -----------------------------------------------------------------------------
9
10import re
11import sys
12import warnings
13
14from IPython.core.oinspect import OInfo
15
16# -----------------------------------------------------------------------------
17# Main function
18# -----------------------------------------------------------------------------
19
20# RegExp for splitting line contents into pre-char//first word-method//rest.
21# For clarity, each group in on one line.
22
23# WARNING: update the regexp if the escapes in interactiveshell are changed, as
24# they are hardwired in.
25
26# Although it's not solely driven by the regex, note that:
27# ,;/% only trigger if they are the first character on the line
28# ! and !! trigger if they are first char(s) *or* follow an indent
29# ? triggers as first or last char.
30
31line_split = re.compile(
32 r"""
33 ^(\s*) # any leading space
34 ([,;/%]|!!?|\?\??)? # escape character or characters
35 \s*(%{0,2}[\w\.\*]*) # function/method, possibly with leading %
36 # to correctly treat things like '?%magic'
37 (.*?$|$) # rest of line
38 """,
39 re.VERBOSE,
40)
41
42
43def split_user_input(
44 line: str, pattern: re.Pattern[str] | None = None
45) -> tuple[str, str, str, str]:
46 """Split user input into initial whitespace, escape character, function part
47 and the rest.
48 """
49 assert isinstance(line, str)
50
51 if pattern is None:
52 pattern = line_split
53 match = pattern.match(line)
54 if not match:
55 # print("match failed for line '%s'" % line)
56 try:
57 ifun, the_rest = line.split(None, 1)
58 except ValueError:
59 # print("split failed for line '%s'" % line)
60 ifun, the_rest = line, ""
61 pre = re.match(r"^(\s*)(.*)", line).groups()[0]
62 esc = ""
63 else:
64 pre, esc, ifun, the_rest = match.groups()
65
66 # print('line:<%s>' % line) # dbg
67 # print('pre <%s> ifun <%s> rest <%s>' % (pre,ifun.strip(),the_rest)) # dbg
68 return pre, esc or "", ifun.strip(), the_rest
69
70
71class LineInfo:
72 """A single line of input and associated info.
73
74 Includes the following as properties:
75
76 line
77 The original, raw line
78
79 continue_prompt
80 Is this line a continuation in a sequence of multiline input?
81
82 pre
83 Any leading whitespace.
84
85 esc
86 The escape character(s) in pre or the empty string if there isn't one.
87 Note that '!!' and '??' are possible values for esc. Otherwise it will
88 always be a single character.
89
90 ifun
91 The 'function part', which is basically the maximal initial sequence
92 of valid python identifiers and the '.' character. This is what is
93 checked for alias and magic transformations, used for auto-calling,
94 etc. In contrast to Python identifiers, it may start with "%" and contain
95 "*".
96
97 the_rest
98 Everything else on the line.
99
100 raw_the_rest
101 the_rest without whitespace stripped.
102 """
103
104 line: str
105 continue_prompt: bool
106 pre: str
107 esc: str
108 ifun: str
109 raw_the_rest: str
110 the_rest: str
111 pre_char: str
112 pre_whitespace: str
113
114 def __init__(self, line: str, continue_prompt: bool = False) -> None:
115 assert isinstance(line, str)
116 self.line = line
117 self.continue_prompt = continue_prompt
118 self.pre, self.esc, self.ifun, self.raw_the_rest = split_user_input(line)
119 self.the_rest = self.raw_the_rest.lstrip()
120
121 self.pre_char = self.pre.strip()
122 if self.pre_char:
123 self.pre_whitespace = "" # No whitespace allowed before esc chars
124 else:
125 self.pre_whitespace = self.pre
126
127 def ofind(self, ip) -> OInfo:
128 """Do a full, attribute-walking lookup of the ifun in the various
129 namespaces for the given IPython InteractiveShell instance.
130
131 Return a dict with keys: {found, obj, ospace, ismagic}
132
133 Note: can cause state changes because of calling getattr, but should
134 only be run if autocall is on and if the line hasn't matched any
135 other, less dangerous handlers.
136
137 Does cache the results of the call, so can be called multiple times
138 without worrying about *further* damaging state.
139
140 .. deprecated:: 9.8
141 Use ``shell._ofind(line_info.ifun)`` directly instead.
142 """
143 warnings.warn(
144 "LineInfo.ofind() is deprecated since IPython 9.9. "
145 "Use shell._ofind(line_info.ifun) directly instead.",
146 DeprecationWarning,
147 stacklevel=2,
148 )
149 return ip._ofind(self.ifun)
150
151 def __str__(self) -> str:
152 return "LineInfo [%s|%s|%s|%s]" % (self.pre, self.esc, self.ifun, self.the_rest)
153
154 def __repr__(self) -> str:
155 return "<" + str(self) + ">"