{ "cells": [ { "cell_type": "markdown", "id": "9423392d", "metadata": {}, "source": [ "# Intro\n", "\n", "`python-code-data` is a Python package which provides a way to convert a Python\n", "\"code object\" into data that can be introspected and changed. It provides\n", "a higher level API than manipulating code objects directly, but faithfully\n", "maintains the full semantics of the code object for Python 3.7-3.10.\n", "\n", "- Tested to make sure the `CodeData` object is isomorphic to the original\n", " code object on all installed modules and using generative testing with Hypothesis (using [hypothesmith](https://github.com/Zac-HD/hypothesmith#hypothesmith) to generate Python code).\n", "- Decodes flags and bytecode into a human readable form.\n", "- Hashable, just like the original code object.\n", "- Provides a CLI to introspect Python objects from the command line, with\n", " colored pretty printing courtesy of Rich.\n", "- Able to encode to/from JSON faithfully\n", "\n", "It is meant to be used by anyone trying to understand Python code to build some sort of compiler, for tools like Numba.\n", "\n", "First install the package, alongside rich for pretty printing:\n", "\n", "```bash\n", "pip install python-code-data[rich]\n", "```\n", "\n", "Then try inspecting some code objects:" ] }, { "cell_type": "code", "execution_count": 1, "id": "c5ea520d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "CodeData(\n", " (\n", " (\n", " Instruction('LOAD_FAST', Varname('x'), line_number=8),\n", " Instruction(\n", " 'LOAD_CONST',\n", " Constant(1, _index_override=1),\n", " line_number=8\n", " ),\n", " Instruction('BINARY_ADD', line_number=8),\n", " Instruction(\n", " 'STORE_FAST',\n", " Varname('y', _index_override=1),\n", " line_number=8\n", " ),\n", " Instruction(\n", " 'LOAD_FAST',\n", " Varname('y', _index_override=1),\n", " line_number=9\n", " ),\n", " Instruction('RETURN_VALUE', line_number=9)\n", " ),\n", " ),\n", " filename='/tmp/ipykernel_725/489739762.py',\n", " first_line_number=7,\n", " name='fn',\n", " stacksize=2,\n", " type=Function(Args(positional_or_keyword=('x',))),\n", " _additional_args=(Constant(None, _index_override=0),)\n", ")\n", "\n" ], "text/plain": [ "\n", "\u001b[1mCodeData\u001b[0m\u001b[1m(\u001b[0m\n", " \u001b[1m(\u001b[0m\n", " \u001b[1m(\u001b[0m\n", " \u001b[1mInstruction\u001b[0m\u001b[1m(\u001b[0m'LOAD_FAST', \u001b[1mVarname\u001b[0m\u001b[1m(\u001b[0m'x'\u001b[1m)\u001b[0m, line_number=\u001b[1m8\u001b[0m\u001b[1m)\u001b[0m,\n", " \u001b[1mInstruction\u001b[0m\u001b[1m(\u001b[0m\n", " 'LOAD_CONST',\n", " \u001b[1mConstant\u001b[0m\u001b[1m(\u001b[0m\u001b[1m1\u001b[0m, _index_override=\u001b[1m1\u001b[0m\u001b[1m)\u001b[0m,\n", " line_number=\u001b[1m8\u001b[0m\n", " \u001b[1m)\u001b[0m,\n", " \u001b[1mInstruction\u001b[0m\u001b[1m(\u001b[0m'BINARY_ADD', line_number=\u001b[1m8\u001b[0m\u001b[1m)\u001b[0m,\n", " \u001b[1mInstruction\u001b[0m\u001b[1m(\u001b[0m\n", " 'STORE_FAST',\n", " \u001b[1mVarname\u001b[0m\u001b[1m(\u001b[0m'y', _index_override=\u001b[1m1\u001b[0m\u001b[1m)\u001b[0m,\n", " line_number=\u001b[1m8\u001b[0m\n", " \u001b[1m)\u001b[0m,\n", " \u001b[1mInstruction\u001b[0m\u001b[1m(\u001b[0m\n", " 'LOAD_FAST',\n", " \u001b[1mVarname\u001b[0m\u001b[1m(\u001b[0m'y', _index_override=\u001b[1m1\u001b[0m\u001b[1m)\u001b[0m,\n", " line_number=\u001b[1m9\u001b[0m\n", " \u001b[1m)\u001b[0m,\n", " \u001b[1mInstruction\u001b[0m\u001b[1m(\u001b[0m'RETURN_VALUE', line_number=\u001b[1m9\u001b[0m\u001b[1m)\u001b[0m\n", " \u001b[1m)\u001b[0m,\n", " \u001b[1m)\u001b[0m,\n", " filename='/tmp/ipykernel_725/489739762.py',\n", " first_line_number=\u001b[1m7\u001b[0m,\n", " name='fn',\n", " stacksize=\u001b[1m2\u001b[0m,\n", " type=\u001b[1mFunction\u001b[0m\u001b[1m(\u001b[0m\u001b[1mArgs\u001b[0m\u001b[1m(\u001b[0mpositional_or_keyword=\u001b[1m(\u001b[0m'x',\u001b[1m)\u001b[0m\u001b[1m)\u001b[0m\u001b[1m)\u001b[0m,\n", " _additional_args=\u001b[1m(\u001b[0m\u001b[1mConstant\u001b[0m\u001b[1m(\u001b[0m\u001b[3mNone\u001b[0m, _index_override=\u001b[1m0\u001b[0m\u001b[1m)\u001b[0m,\u001b[1m)\u001b[0m\n", "\u001b[1m)\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# 1. Install rich hooks for prettier printing\n", "from rich import pretty\n", "pretty.install()\n", "\n", "\n", "# 2. Get a code object\n", "def fn(x):\n", " y = x + 1\n", " return y\n", "\n", "\n", "# 3. Convert it to a dataclass!\n", "from code_data import CodeData\n", "\n", "CodeData.from_code(fn.__code__)" ] } ], "metadata": { "jupytext": { "cell_metadata_filter": "-all", "formats": "md:myst", "text_representation": { "extension": ".md", "format_name": "myst", "format_version": 0.13, "jupytext_version": "1.11.5" } }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.8" }, "source_map": [ 14, 41 ] }, "nbformat": 4, "nbformat_minor": 5 }