Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/IPython/core/debugger_backport.py: 11%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

81 statements  

1""" 

2The code in this module is a backport of cPython changes in Pdb 

3that were introduced in Python 3.13 by gh-83151: Make closure work on pdb 

4https://github.com/python/cpython/pull/111094. 

5This file should be removed once IPython drops supports for Python 3.12. 

6 

7The only changes are: 

8- reformatting by darker (black) formatter 

9- addition of type-ignore comments to satisfy mypy 

10 

11Copyright (c) 2001 Python Software Foundation; All Rights Reserved 

12 

13PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 

14-------------------------------------------- 

15 

161. This LICENSE AGREEMENT is between the Python Software Foundation 

17("PSF"), and the Individual or Organization ("Licensee") accessing and 

18otherwise using this software ("Python") in source or binary form and 

19its associated documentation. 

20 

212. Subject to the terms and conditions of this License Agreement, PSF hereby 

22grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, 

23analyze, test, perform and/or display publicly, prepare derivative works, 

24distribute, and otherwise use Python alone or in any derivative version, 

25provided, however, that PSF's License Agreement and PSF's notice of copyright, 

26i.e., "Copyright (c) 2001 Python Software Foundation; All Rights Reserved" 

27are retained in Python alone or in any derivative version prepared by Licensee. 

28 

293. In the event Licensee prepares a derivative work that is based on 

30or incorporates Python or any part thereof, and wants to make 

31the derivative work available to others as provided herein, then 

32Licensee hereby agrees to include in any such work a brief summary of 

33the changes made to Python. 

34 

354. PSF is making Python available to Licensee on an "AS IS" 

36basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 

37IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND 

38DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 

39FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT 

40INFRINGE ANY THIRD PARTY RIGHTS. 

41 

425. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 

43FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS 

44A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, 

45OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 

46 

476. This License Agreement will automatically terminate upon a material 

48breach of its terms and conditions. 

49 

507. Nothing in this License Agreement shall be deemed to create any 

51relationship of agency, partnership, or joint venture between PSF and 

52Licensee. This License Agreement does not grant permission to use PSF 

53trademarks or trade name in a trademark sense to endorse or promote 

54products or services of Licensee, or any third party. 

55 

568. By copying, installing or otherwise using Python, Licensee 

57agrees to be bound by the terms and conditions of this License 

58Agreement. 

59""" 

60 

61import sys 

62import types 

63import codeop 

64import textwrap 

65from types import CodeType 

66 

67 

68class PdbClosureBackport: 

69 def _exec_in_closure(self, source, globals, locals): # type: ignore[no-untyped-def] 

70 """Run source code in closure so code object created within source 

71 can find variables in locals correctly 

72 returns True if the source is executed, False otherwise 

73 """ 

74 

75 # Determine if the source should be executed in closure. Only when the 

76 # source compiled to multiple code objects, we should use this feature. 

77 # Otherwise, we can just raise an exception and normal exec will be used. 

78 

79 code = compile(source, "<string>", "exec") 

80 if not any(isinstance(const, CodeType) for const in code.co_consts): 

81 return False 

82 

83 # locals could be a proxy which does not support pop 

84 # copy it first to avoid modifying the original locals 

85 locals_copy = dict(locals) 

86 

87 locals_copy["__pdb_eval__"] = {"result": None, "write_back": {}} 

88 

89 # If the source is an expression, we need to print its value 

90 try: 

91 compile(source, "<string>", "eval") 

92 except SyntaxError: 

93 pass 

94 else: 

95 source = "__pdb_eval__['result'] = " + source 

96 

97 # Add write-back to update the locals 

98 source = ( 

99 "try:\n" 

100 + textwrap.indent(source, " ") 

101 + "\n" 

102 + "finally:\n" 

103 + " __pdb_eval__['write_back'] = locals()" 

104 ) 

105 

106 # Build a closure source code with freevars from locals like: 

107 # def __pdb_outer(): 

108 # var = None 

109 # def __pdb_scope(): # This is the code object we want to execute 

110 # nonlocal var 

111 # <source> 

112 # return __pdb_scope.__code__ 

113 source_with_closure = ( 

114 "def __pdb_outer():\n" 

115 + "\n".join(f" {var} = None" for var in locals_copy) 

116 + "\n" 

117 + " def __pdb_scope():\n" 

118 + "\n".join(f" nonlocal {var}" for var in locals_copy) 

119 + "\n" 

120 + textwrap.indent(source, " ") 

121 + "\n" 

122 + " return __pdb_scope.__code__" 

123 ) 

124 

125 # Get the code object of __pdb_scope() 

126 # The exec fills locals_copy with the __pdb_outer() function and we can call 

127 # that to get the code object of __pdb_scope() 

128 ns = {} 

129 try: 

130 exec(source_with_closure, {}, ns) 

131 except Exception: 

132 return False 

133 code = ns["__pdb_outer"]() 

134 

135 cells = tuple(types.CellType(locals_copy.get(var)) for var in code.co_freevars) 

136 

137 try: 

138 exec(code, globals, locals_copy, closure=cells) 

139 except Exception: 

140 return False 

141 

142 # get the data we need from the statement 

143 pdb_eval = locals_copy["__pdb_eval__"] 

144 

145 # __pdb_eval__ should not be updated back to locals 

146 pdb_eval["write_back"].pop("__pdb_eval__") 

147 

148 # Write all local variables back to locals 

149 locals.update(pdb_eval["write_back"]) 

150 eval_result = pdb_eval["result"] 

151 if eval_result is not None: 

152 print(repr(eval_result)) 

153 

154 return True 

155 

156 def default(self, line): # type: ignore[no-untyped-def] 

157 if line[:1] == "!": 

158 line = line[1:].strip() 

159 locals = self.curframe_locals 

160 globals = self.curframe.f_globals 

161 try: 

162 buffer = line 

163 if ( 

164 code := codeop.compile_command(line + "\n", "<stdin>", "single") 

165 ) is None: 

166 # Multi-line mode 

167 with self._disable_command_completion(): 

168 buffer = line 

169 continue_prompt = "... " 

170 while ( 

171 code := codeop.compile_command(buffer, "<stdin>", "single") 

172 ) is None: 

173 if self.use_rawinput: 

174 try: 

175 line = input(continue_prompt) 

176 except (EOFError, KeyboardInterrupt): 

177 self.lastcmd = "" 

178 print("\n") 

179 return 

180 else: 

181 self.stdout.write(continue_prompt) 

182 self.stdout.flush() 

183 line = self.stdin.readline() 

184 if not len(line): 

185 self.lastcmd = "" 

186 self.stdout.write("\n") 

187 self.stdout.flush() 

188 return 

189 else: 

190 line = line.rstrip("\r\n") 

191 buffer += "\n" + line 

192 save_stdout = sys.stdout 

193 save_stdin = sys.stdin 

194 save_displayhook = sys.displayhook 

195 try: 

196 sys.stdin = self.stdin 

197 sys.stdout = self.stdout 

198 sys.displayhook = self.displayhook 

199 if not self._exec_in_closure(buffer, globals, locals): 

200 exec(code, globals, locals) 

201 finally: 

202 sys.stdout = save_stdout 

203 sys.stdin = save_stdin 

204 sys.displayhook = save_displayhook 

205 except: 

206 self._error_exc()