Format: application/x-python-code
⬇ Download File (9.9 KB)
   1  o
   2  
   3  ��h7(�@s�dZdZddlmZmZmZmZmZmZm	Z	m
   4  Z
   5  mZddlZddlZddl
   6  mZmZmZdd�ZGdd	�d	e�ZGd
   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�UnioncCs|dvrdSdS)N)�__ret__�__code__FT���namerr�Qc:\Users\shubh\Videos\agent\nova\Lib\site-packages\IPython/core/magics/ast_mod.py�<lambda>�src@s~eZdZUdZdZeed<dZeed<dd�Zdd	d
 184  �Z	dd�Z
 185  d
 186  d�Zdefdd�Z
 187  defdd�Zdeeeffdd�ZdS)�Manglerzb
 188      Mangle given names in and ast tree to make sure they do not conflict with
 189      user code.
 190      T�enabledF�debugcOs|jrt|i|��dSdS�N)r�print)�self�args�kwargsrrr�log�s�zMangler.logNcCs|durdd�}||_dS)NcSs
 191  |�d�S)NZ___)�
 192  startswithrrrrr�s
 193  z"Mangler.__init__.<locals>.<lambda>��	predicate)rr rrr�__init__�s
 194  zMangler.__init__cCs<|�|j�r|�d|j�d|j|_|S|�d|j�|S)N�Mangling�mangle-�Not mangling)r �idr�r�noderrr�
 195  visit_Name�s�zMangler.visit_NamecCs�|�|j�r|�d|j�d|j|_n|�d|j�|jjD]}|�|j�r5|�d|j�d|j|_q|�d|j�q|�|�S)Nr"r#r$zMangling function argzNot mangling function arg)r rrr�arg�
 196  generic_visit)rr'r)rrr�visit_FunctionDef�s
 197  zMangler.visit_FunctionDefr'cC�
 198  |�|�Sr��_visit_Import_and_ImportFromr&rrr�visit_ImportFrom��
 199  zMangler.visit_ImportFromcCr,rr-r&rrr�visit_Import�r0zMangler.visit_ImportcCsZ|jD]'}|jdur
 200  |jn|j}|�|�r#d|}|�d|�||_q|�d|j�q|S)Nr#zMangling AliaszNot mangling Alias)�names�asnamerr r)rr'�aliasr3�new_namerrrr.�s
 201  
 202  z$Mangler._visit_Import_and_ImportFromr)�__name__�
 203  __module__�__qualname__�__doc__r�bool�__annotations__rrr!r(r+r
 204  r/r	r1r
 205  r.rrrrr�s
 206  
 207  
 208  rc@sveZdZUdZeed<dZeed<eed<	ddede	e
 209  fd	d
 210  �Ze	dde
 211  de	e
 212  fdd��Zd
 213  d�Zdd�ZdS)�ReplaceCodeTransformerTrFr�manglerN�template�mappingcCslt|ttd�f�sJ�t|td�tdd��f�sJ�t|tj�s"J�||_t|d�|_|dur1i}||_dS)NcSsdSrrrrrrrsz1ReplaceCodeTransformer.__init__.<locals>.<lambda>r)	�
 214  isinstance�dict�type�astrr>rr=r?)rr>r?�mangling_predicaterrrr!s
 215  zReplaceCodeTransformer.__init__cCs|t�|�||d�S)N)r?rD)rC�parse)�clsr>r?rDrrr�from_strings�z"ReplaceCodeTransformer.from_stringcCs�|js|S|jd}t|t�r?|j��|j�ttdt�d�g|j	d��t
 216  �|�ttdt�d�d�}t
 217  �|�}||j
 218  d<nt
 219  �d�jd|j
 220  d<|j|j
 221  d<t
 222  �|j�}t�|�}|j�|�}|�|�}t
 223  �|�}|jr}td	�tt
 224  �|��td
 225  �|S)N�����zret-tmp)�ctx)�valuer�Nonerrz---- Transformed code ----z---- ---------------- ----)r�bodyr@r�pop�appendrrrrJrC�fix_missing_locationsrr?rEr>�copy�deepcopyr=�visitr*rr�unparse)r�code�last�retZtplZtxr'Znode_2rrr�visit_Modules,
 226  
 227  
 228  "
 229  
 230  
 231  
 232  
 233  z#ReplaceCodeTransformer.visit_ModulecCsJt|jt�r |jj|jvr |j|jjdur t�|j|jj�S|�|�Sr)r@rJrr%r?rPrQr*)r�exprrrr�
 234  visit_ExprFs
 235  z!ReplaceCodeTransformer.visit_Expr)NN)r6r7r8rr:r;rrrrrr!�classmethod�strrGrWrYrrrrr<s&
 236  ��
 237  ����'r<)r9Z__skip_doctest__rCrrrrrrrr	r
 238  rP�typingrrr
 239  Z
 240  mangle_allrr<rrrr�<module>s2,=