• Stars
    star
    312
  • Rank 131,234 (Top 3 %)
  • Language
    Python
  • License
    MIT License
  • Created about 5 years ago
  • Updated 28 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Get information about what a Python frame is currently doing, particularly the AST node being executed

executing

Build Status Coverage Status Supports Python versions 2.7 and 3.5+, including PyPy

This mini-package lets you get information about what a frame is currently doing, particularly the AST node being executed.

Usage

Getting the AST node

import executing

node = executing.Source.executing(frame).node

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).

node will always be the same instance for multiple calls with frames at the same point of execution.

If you have a traceback object, pass it directly to Source.executing() rather than the tb_frame attribute to get the correct node.

Getting the source code of the node

For this you will need to separately install the asttokens library, then obtain an ASTTokens object:

executing.Source.executing(frame).source.asttokens()

or:

executing.Source.for_frame(frame).asttokens()

or use one of the convenience methods:

executing.Source.executing(frame).text()
executing.Source.executing(frame).text_range()

Getting the __qualname__ of the current function

executing.Source.executing(frame).code_qualname()

or:

executing.Source.for_frame(frame).code_qualname(frame.f_code)

The Source class

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.

Installation

pip install executing

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).

How does it work?

Suppose the frame is executing this line:

self.foo(bar.x)

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?

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:

(self.foo ** 'longuniqueconstant')(bar.x)

and compile it, and the bytecode will be almost the same but there will be two new instructions:

LOAD_CONST 'longuniqueconstant'
BINARY_POWER

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.

Is it reliable?

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) with real code. Specifically, for each file, the tests:

  1. Identify as many nodes as possible from all the bytecode instructions in the file, and assert that they are all distinct
  2. Find all the nodes that should be identifiable, and assert that they were indeed identified somewhere

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.

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.

Which nodes can it identify?

Currently it works in almost all cases for the following ast nodes:

  • Call, e.g. self.foo(bar)
  • Attribute, e.g. point.x
  • Subscript, e.g. lst[1]
  • BinOp, e.g. x + y (doesn't include and and or)
  • UnaryOp, e.g. -n (includes not but only works sometimes)
  • Compare e.g. a < b (not for chains such as 0 < p < 1)

The plan is to extend to more operations in the future.

Projects that use this

My Projects

  • 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.
  • futurecoder: Highlights the executing node in tracebacks using executing via stack_data, and provides debugging with snoop.
  • 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.
  • 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.
  • sorcery: Dark magic delights in Python. Uses executing to let special callables called spells know where they're being called from.

Projects I've contributed to

  • IPython: Highlights the executing node in tracebacks using executing via stack_data.
  • icecream: 🍦 Sweet and creamy print debugging. Uses executing to identify where ic is called and print its arguments.
  • friendly_traceback: Uses stack_data and executing to pinpoint the cause of errors and provide helpful explanations.
  • python-devtools: Uses executing for print debugging similar to icecream.
  • sentry_sdk: Add the integration sentry_sdk.integrations.executingExecutingIntegration() to show the function __qualname__ in each frame in sentry events.
  • varname: Dark magics about variable names in python. Uses executing to find where its various magical functions like varname and nameof are called from.

More Repositories

1

heartrate

Simple real time visualisation of the execution of a Python program.
Python
1,624
star
2

birdseye

Graphical Python debugger which lets you easily view the values of all evaluated expressions
JavaScript
1,570
star
3

futurecoder

100% free and interactive Python course for beginners
Python
1,245
star
4

snoop

A powerful set of Python debugging tools, based on PySnooper
Python
927
star
5

sorcery

Dark magic delights in Python
Python
353
star
6

s3-stream-upload

Manages streaming of data to AWS S3 without knowing the size beforehand and without keeping it all in memory or writing to disk.
Java
200
star
7

funcfinder

A tool for automatically solving problems of the form "I need a python function that does X."
Python
165
star
8

instant_api

Instantly create an HTTP API with automatic type conversions, JSON RPC, and a Swagger UI. Just add methods!
Python
126
star
9

stack_data

Python
32
star
10

birdseye-pycharm

IntelliJ IDE plugin for the Python debugger birdseye
Java
31
star
11

pure_eval

Safely evaluate AST nodes without side effects
Python
26
star
12

outdated

Check if a version of a PyPI package is outdated
Python
22
star
13

cheap_repr

Better version of repr/reprlib for short, cheap string representations in Python
Python
21
star
14

friendly_states

Declarative, explicit, tool-friendly finite state machines in Python
Python
19
star
15

nameof

Python function to get the name of a variable or attribute, as in C#
Python
13
star
16

boxes

A library that adds object oriented power to fields, letting you do better than traditional getters and setters.
Java
12
star
17

sunhours

Sketchup plugin for analysing the amount of sunlight hitting points on a surface over the year:
HTML
10
star
18

pyodide-worker-runner

TypeScript
9
star
19

instant_client

Type safe JSON RPC client with automatic (de)serialization. Best paired with instant_api.
Python
7
star
20

jsonfinder

Python library to easily handle JSON contained within strings.
Python
7
star
21

oeis-explorer

Explore related sequences in the OEIS
Python
6
star
22

sync-message

TypeScript
5
star
23

comsync

TypeScript
4
star
24

python_runner

Helper for running python code indirectly
Python
4
star
25

dryenv

Simple DRY configuration with environment variables and pydantic
Python
4
star
26

littleutils

Small personal collection of python utility functions, partly just for fun.
Python
3
star
27

askso

AskSO - StackOverflow Python Question Assistant
Python
2
star
28

datafunctions

Automatic (de)serialization of arguments and return values for Python functions
Python
2
star
29

quiggles

Android app for drawing symmetrical patterns
Kotlin
2
star
30

alexmojaki

2
star
31

dependent_types

Python
1
star
32

case-classes

A framework to refactor computing a result from an aggregate object
Java
1
star
33

trace_augmentation

Python
1
star