Format: application/x-python-code
1 o
2
3 ��h7( � @ s� d Z dZddlmZmZmZmZmZmZm Z m
4 Z
5 mZ ddlZddlZddl
6 mZmZmZ dd� ZG dd � d e�ZG d
7 d� de�ZdS )a�
8 This module contains utility function and classes to inject simple ast
9 transformations based on code strings into IPython. While it is already possible
10 with ast-transformers it is not easy to directly manipulate ast.
11
12
13 IPython has pre-code and post-code hooks, but are ran from within the IPython
14 machinery so may be inappropriate, for example for performance measurement.
15
16 This module give you tools to simplify this, and expose 2 classes:
17
18 - `ReplaceCodeTransformer` which is a simple ast transformer based on code
19 template,
20
21 and for advance case:
22
23 - `Mangler` which is a simple ast transformer that mangle names in the ast.
24
25
26 Example, let's try to make a simple version of the ``timeit`` magic, that run a
27 code snippet 10 times and print the average time taken.
28
29 Basically we want to run :
30
31 .. code-block:: python
32
33 from time import perf_counter
34 now = perf_counter()
35 for i in range(10):
36 __code__ # our code
37 print(f"Time taken: {(perf_counter() - now)/10}")
38 __ret__ # the result of the last statement
39
40 Where ``__code__`` is the code snippet we want to run, and ``__ret__`` is the
41 result, so that if we for example run `dataframe.head()` IPython still display
42 the head of dataframe instead of nothing.
43
44 Here is a complete example of a file `timit2.py` that define such a magic:
45
46 .. code-block:: python
47
48 from IPython.core.magic import (
49 Magics,
50 magics_class,
51 line_cell_magic,
52 )
53 from IPython.core.magics.ast_mod import ReplaceCodeTransformer
54 from textwrap import dedent
55 import ast
56
57 template = template = dedent('''
58 from time import perf_counter
59 now = perf_counter()
60 for i in range(10):
61 __code__
62 print(f"Time taken: {(perf_counter() - now)/10}")
63 __ret__
64 '''
65 )
66
67
68 @magics_class
69 class AstM(Magics):
70 @line_cell_magic
71 def t2(self, line, cell):
72 transformer = ReplaceCodeTransformer.from_string(template)
73 transformer.debug = True
74 transformer.mangler.debug = True
75 new_code = transformer.visit(ast.parse(cell))
76 return exec(compile(new_code, "<ast>", "exec"))
77
78
79 def load_ipython_extension(ip):
80 ip.register_magics(AstM)
81
82
83
84 .. code-block:: python
85
86 In [1]: %load_ext timit2
87
88 In [2]: %%t2
89 ...: import time
90 ...: time.sleep(0.05)
91 ...:
92 ...:
93 Time taken: 0.05435649999999441
94
95
96 If you wish to ran all the code enter in IPython in an ast transformer, you can
97 do so as well:
98
99 .. code-block:: python
100
101 In [1]: from IPython.core.magics.ast_mod import ReplaceCodeTransformer
102 ...:
103 ...: template = '''
104 ...: from time import perf_counter
105 ...: now = perf_counter()
106 ...: __code__
107 ...: print(f"Code ran in {perf_counter()-now}")
108 ...: __ret__'''
109 ...:
110 ...: get_ipython().ast_transformers.append(ReplaceCodeTransformer.from_string(template))
111
112 In [2]: 1+1
113 Code ran in 3.40410006174352e-05
114 Out[2]: 2
115
116
117
118 Hygiene and Mangling
119 --------------------
120
121 The ast transformer above is not hygienic, it may not work if the user code use
122 the same variable names as the ones used in the template. For example.
123
124 To help with this by default the `ReplaceCodeTransformer` will mangle all names
125 staring with 3 underscores. This is a simple heuristic that should work in most
126 case, but can be cumbersome in some case. We provide a `Mangler` class that can
127 be overridden to change the mangling heuristic, or simply use the `mangle_all`
128 utility function. It will _try_ to mangle all names (except `__ret__` and
129 `__code__`), but this include builtins (``print``, ``range``, ``type``) and
130 replace those by invalid identifiers py prepending ``mangle-``:
131 ``mangle-print``, ``mangle-range``, ``mangle-type`` etc. This is not a problem
132 as currently Python AST support invalid identifiers, but it may not be the case
133 in the future.
134
135 You can set `ReplaceCodeTransformer.debug=True` and
136 `ReplaceCodeTransformer.mangler.debug=True` to see the code after mangling and
137 transforming:
138
139 .. code-block:: python
140
141
142 In [1]: from IPython.core.magics.ast_mod import ReplaceCodeTransformer, mangle_all
143 ...:
144 ...: template = '''
145 ...: from builtins import type, print
146 ...: from time import perf_counter
147 ...: now = perf_counter()
148 ...: __code__
149 ...: print(f"Code ran in {perf_counter()-now}")
150 ...: __ret__'''
151 ...:
152 ...: transformer = ReplaceCodeTransformer.from_string(template, mangling_predicate=mangle_all)
153
154
155 In [2]: transformer.debug = True
156 ...: transformer.mangler.debug = True
157 ...: get_ipython().ast_transformers.append(transformer)
158
159 In [3]: 1+1
160 Mangling Alias mangle-type
161 Mangling Alias mangle-print
162 Mangling Alias mangle-perf_counter
163 Mangling now
164 Mangling perf_counter
165 Not mangling __code__
166 Mangling print
167 Mangling perf_counter
168 Mangling now
169 Not mangling __ret__
170 ---- Transformed code ----
171 from builtins import type as mangle-type, print as mangle-print
172 from time import perf_counter as mangle-perf_counter
173 mangle-now = mangle-perf_counter()
174 ret-tmp = 1 + 1
175 mangle-print(f'Code ran in {mangle-perf_counter() - mangle-now}')
176 ret-tmp
177 ---- ---------------- ----
178 Code ran in 0.00013654199938173406
179 Out[3]: 2
180
181
182 T� ) �NodeTransformer�Store�Load�Name�Expr�Assign�Module�Import�
183 ImportFromN)�Dict�Optional�Unionc C s | dv rdS dS )N)�__ret__�__code__FT� ��namer r �Qc:\Users\shubh\Videos\agent\nova\Lib\site-packages\IPython/core/magics/ast_mod.py�<lambda>� s r c @ s~ e Zd ZU dZdZeed<