Format: application/octet-stream
⬇ Download File (8.7 KB)
   1  Metadata-Version: 2.1
   2  Name: executing
   3  Version: 2.2.0
   4  Summary: Get the currently executing AST node of a frame, and other information
   5  Home-page: https://github.com/alexmojaki/executing
   6  Author: Alex Hall
   7  Author-email: alex.mojaki@gmail.com
   8  License: MIT
   9  Classifier: License :: OSI Approved :: MIT License
  10  Classifier: Programming Language :: Python
  11  Classifier: Programming Language :: Python :: 3
  12  Classifier: Programming Language :: Python :: 3.8
  13  Classifier: Programming Language :: Python :: 3.9
  14  Classifier: Programming Language :: Python :: 3.10
  15  Classifier: Programming Language :: Python :: 3.11
  16  Classifier: Programming Language :: Python :: 3.12
  17  Classifier: Programming Language :: Python :: 3.13
  18  Requires-Python: >=3.8
  19  Description-Content-Type: text/markdown
  20  License-File: LICENSE.txt
  21  Provides-Extra: tests
  22  Requires-Dist: asttokens>=2.1.0; extra == "tests"
  23  Requires-Dist: ipython; extra == "tests"
  24  Requires-Dist: pytest; extra == "tests"
  25  Requires-Dist: coverage; extra == "tests"
  26  Requires-Dist: coverage-enable-subprocess; extra == "tests"
  27  Requires-Dist: littleutils; extra == "tests"
  28  Requires-Dist: rich; python_version >= "3.11" and extra == "tests"
  29  
  30  # executing
  31  
  32  [![Build Status](https://github.com/alexmojaki/executing/workflows/Tests/badge.svg?branch=master)](https://github.com/alexmojaki/executing/actions) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/executing/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/executing?branch=master) [![Supports Python versions 3.5+, including PyPy](https://img.shields.io/pypi/pyversions/executing.svg)](https://pypi.python.org/pypi/executing)
  33  
  34  This mini-package lets you get information about what a frame is currently doing, particularly the AST node being executed.
  35  
  36  * [Usage](#usage)
  37      * [Getting the AST node](#getting-the-ast-node)
  38      * [Getting the source code of the node](#getting-the-source-code-of-the-node)
  39      * [Getting the `__qualname__` of the current function](#getting-the-__qualname__-of-the-current-function)
  40      * [The Source class](#the-source-class)
  41  * [Installation](#installation)
  42  * [How does it work?](#how-does-it-work)
  43  * [Is it reliable?](#is-it-reliable)
  44  * [Which nodes can it identify?](#which-nodes-can-it-identify)
  45  * [Projects that use this](#projects-that-use-this)
  46  
  47  ## Usage
  48  
  49  ### Getting the AST node
  50  
  51  ```python
  52  import executing
  53  
  54  node = executing.Source.executing(frame).node
  55  ```
  56  
  57  Then `node` will be an AST node (from the `ast` standard library module) or None if the node couldn't be identified (which may happen often and should always be checked).
  58  
  59  `node` will always be the same instance for multiple calls with frames at the same point of execution.
  60  
  61  If you have a traceback object, pass it directly to `Source.executing()` rather than the `tb_frame` attribute to get the correct node.
  62  
  63  ### Getting the source code of the node
  64  
  65  For this you will need to separately install the [`asttokens`](https://github.com/gristlabs/asttokens) library, then obtain an `ASTTokens` object:
  66  
  67  ```python
  68  executing.Source.executing(frame).source.asttokens()
  69  ```
  70  
  71  or:
  72  
  73  ```python
  74  executing.Source.for_frame(frame).asttokens()
  75  ```
  76  
  77  or use one of the convenience methods:
  78  
  79  ```python
  80  executing.Source.executing(frame).text()
  81  executing.Source.executing(frame).text_range()
  82  ```
  83  
  84  ### Getting the `__qualname__` of the current function
  85  
  86  ```python
  87  executing.Source.executing(frame).code_qualname()
  88  ```
  89  
  90  or:
  91  
  92  ```python
  93  executing.Source.for_frame(frame).code_qualname(frame.f_code)
  94  ```
  95  
  96  ### The `Source` class
  97  
  98  Everything goes through the `Source` class. Only one instance of the class is created for each filename. Subclassing it to add more attributes on creation or methods is recommended. The classmethods such as `executing` will respect this. See the source code and docstrings for more detail.
  99  
 100  ## Installation
 101  
 102      pip install executing
 103  
 104  If you don't like that you can just copy the file `executing.py`, there are no dependencies (but of course you won't get updates).
 105  
 106  ## How does it work?
 107  
 108  Suppose the frame is executing this line:
 109  
 110  ```python
 111  self.foo(bar.x)
 112  ```
 113  
 114  and in particular it's currently obtaining the attribute `self.foo`. Looking at the bytecode, specifically `frame.f_code.co_code[frame.f_lasti]`, we can tell that it's loading an attribute, but it's not obvious which one. We can narrow down the statement being executed using `frame.f_lineno` and find the two `ast.Attribute` nodes representing `self.foo` and `bar.x`. How do we find out which one it is, without recreating the entire compiler in Python?
 115  
 116  The trick is to modify the AST slightly for each candidate expression and observe the changes in the bytecode instructions. We change the AST to this:
 117  
 118  ```python
 119  (self.foo ** 'longuniqueconstant')(bar.x)
 120  ```
 121      
 122  and compile it, and the bytecode will be almost the same but there will be two new instructions:
 123  
 124      LOAD_CONST 'longuniqueconstant'
 125      BINARY_POWER
 126  
 127  and just before that will be a `LOAD_ATTR` instruction corresponding to `self.foo`. Seeing that it's in the same position as the original instruction lets us know we've found our match.
 128  
 129  ## Is it reliable?
 130  
 131  Yes - if it identifies a node, you can trust that it's identified the correct one. The tests are very thorough - in addition to unit tests which check various situations directly, there are property tests against a large number of files (see the filenames printed in [this build](https://travis-ci.org/alexmojaki/executing/jobs/557970457)) with real code. Specifically, for each file, the tests:
 132   
 133   1. Identify as many nodes as possible from all the bytecode instructions in the file, and assert that they are all distinct
 134   2. Find all the nodes that should be identifiable, and assert that they were indeed identified somewhere
 135  
 136  In other words, it shows that there is a one-to-one mapping between the nodes and the instructions that can be handled. This leaves very little room for a bug to creep in.
 137  
 138  Furthermore, `executing` checks that the instructions compiled from the modified AST exactly match the original code save for a few small known exceptions. This accounts for all the quirks and optimisations in the interpreter. 
 139  
 140  ## Which nodes can it identify?
 141  
 142  Currently it works in almost all cases for the following `ast` nodes:
 143   
 144   - `Call`, e.g. `self.foo(bar)`
 145   - `Attribute`, e.g. `point.x`
 146   - `Subscript`, e.g. `lst[1]`
 147   - `BinOp`, e.g. `x + y` (doesn't include `and` and `or`)
 148   - `UnaryOp`, e.g. `-n` (includes `not` but only works sometimes)
 149   - `Compare` e.g. `a < b` (not for chains such as `0 < p < 1`)
 150  
 151  The plan is to extend to more operations in the future.
 152  
 153  ## Projects that use this
 154  
 155  ### My Projects
 156  
 157  - **[`stack_data`](https://github.com/alexmojaki/stack_data)**: Extracts data from stack frames and tracebacks, particularly to display more useful tracebacks than the default. Also uses another related library of mine: **[`pure_eval`](https://github.com/alexmojaki/pure_eval)**.
 158  - **[`futurecoder`](https://futurecoder.io/)**: Highlights the executing node in tracebacks using `executing` via `stack_data`, and provides debugging with `snoop`.
 159  - **[`snoop`](https://github.com/alexmojaki/snoop)**: A feature-rich and convenient debugging library. Uses `executing` to show the operation which caused an exception and to allow the `pp` function to display the source of its arguments.
 160  - **[`heartrate`](https://github.com/alexmojaki/heartrate)**: A simple real time visualisation of the execution of a Python program. Uses `executing` to highlight currently executing operations, particularly in each frame of the stack trace.
 161  - **[`sorcery`](https://github.com/alexmojaki/sorcery)**: Dark magic delights in Python. Uses `executing` to let special callables called spells know where they're being called from.
 162  
 163  ### Projects I've contributed to
 164  
 165  - **[`IPython`](https://github.com/ipython/ipython/pull/12150)**: Highlights the executing node in tracebacks using `executing` via [`stack_data`](https://github.com/alexmojaki/stack_data).
 166  - **[`icecream`](https://github.com/gruns/icecream)**: 🍦 Sweet and creamy print debugging. Uses `executing` to identify where `ic` is called and print its arguments.
 167  - **[`friendly_traceback`](https://github.com/friendly-traceback/friendly-traceback)**: Uses `stack_data` and `executing` to pinpoint the cause of errors and provide helpful explanations.
 168  - **[`python-devtools`](https://github.com/samuelcolvin/python-devtools)**: Uses `executing` for print debugging similar to `icecream`.
 169  - **[`sentry_sdk`](https://github.com/getsentry/sentry-python)**: Add the integration `sentry_sdk.integrations.executingExecutingIntegration()` to show the function `__qualname__` in each frame in sentry events.
 170  - **[`varname`](https://github.com/pwwang/python-varname)**: Dark magics about variable names in python. Uses `executing` to find where its various magical functions like `varname` and `nameof` are called from.
 171