Last active
August 28, 2025 03:44
-
-
Save CNSeniorious000/377d7858331039d9f5e79647520821f4 to your computer and use it in GitHub Desktop.
Userland solution for python/cpython#121306
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ast | |
| from typing import override | |
| class ClassTransformer(ast.NodeTransformer): | |
| @override | |
| def visit_ClassDef(self, node: ast.ClassDef): | |
| traverser = ClassBodyTransformer() | |
| node.body = [ | |
| name_lookup_function, | |
| *map(traverser.visit, node.body), | |
| ast.Delete(targets=[ast.Name(id="__name_lookup", ctx=ast.Del())]), | |
| ast.parse(f"False and ( {','.join(traverser.names)} )").body[0], | |
| ] | |
| return ast.fix_missing_locations(node) | |
| class ClassBodyTransformer(ast.NodeTransformer): | |
| def __init__(self): | |
| self.names = set[str]() | |
| @override | |
| def visit_Name(self, node: ast.Name): | |
| if isinstance(node.ctx, ast.Load) and node.id != "__name_lookup": | |
| self.names.add(node.id) | |
| return build_name_lookup(node.id) | |
| return node | |
| @override | |
| def visit_FunctionDef(self, node: ast.FunctionDef): | |
| node.decorator_list = [self.visit(d) for d in node.decorator_list] | |
| self.visit(node.args) | |
| if node.returns: | |
| node.returns = self.visit(node.returns) | |
| return node | |
| visit_AsyncFunctionDef = visit_FunctionDef # type: ignore # noqa: N815 | |
| @override | |
| def visit_Lambda(self, node: ast.Lambda): | |
| self.visit(node.args) | |
| return node | |
| def build_name_lookup(name: str) -> ast.Call: | |
| return ast.Call(func=ast.Name(id="__name_lookup", ctx=ast.Load()), args=[ast.Constant(value=name)], keywords=[]) | |
| name_lookup_source = """ | |
| @lambda factory: factory() | |
| def __name_lookup(): | |
| from builtins import KeyError, NameError | |
| from collections import ChainMap | |
| from inspect import currentframe | |
| f = currentframe().f_back.f_back | |
| c = ChainMap(f.f_locals, e:={}, f.f_globals, f.f_builtins) # LEGB | |
| if freevars := f.f_code.co_freevars: | |
| freevars = {*f.f_code.co_freevars} | |
| while freevars: | |
| f = f.f_back | |
| for name in f.f_code.co_cellvars: | |
| if name in freevars.intersection(f.f_code.co_cellvars): | |
| freevars.remove(name) | |
| e[name] = f.f_locals[name] | |
| def lookup(name): | |
| try: | |
| return c[name] | |
| except KeyError as e: | |
| raise NameError(*e.args) from None | |
| return lookup | |
| """ | |
| name_lookup_function = ast.parse(name_lookup_source).body[0] | |
| # The code above is copied from https://github.com/promplate/pyth-on-line/commit/c28186a0372bbded9c8a2ba250aeef2f6b790178 | |
| def exec_(source: str, /, globals: dict | None = None, locals: dict | None = None): | |
| mod = ClassTransformer().visit(ast.parse(source)) | |
| exec(compile(mod, filename="<exec>", mode="exec"), globals, locals) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment