Created
January 14, 2025 07:24
-
-
Save GreyElaina/d22fd18355b8397e4bc8a1640f18d74f to your computer and use it in GitHub Desktop.
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
| # /// script | |
| # dependencies = [ | |
| # "executing>=2.1.0" | |
| # ] | |
| # /// | |
| import ast | |
| import sys | |
| from dataclasses import dataclass | |
| from executing import Source | |
| from typing import Callable, Literal | |
| _CONVERSIONS = {-1: None, 97: "a", 114: "r", 115: "s"} | |
| @dataclass | |
| class Interpolation: | |
| value: object | |
| expr: str | |
| conv: Literal["a", "r", "s"] | None | |
| format_spec: str | |
| @dataclass | |
| class Template: | |
| args: tuple[str | Interpolation, ...] | |
| def t(cb: Callable[[], str]): | |
| """ | |
| A function that converts an f-string lambda into a Template object with parsed interpolations. | |
| This function takes a lambda expression that returns an f-string and converts it into a Template | |
| object, parsing both literal strings and interpolated values while preserving formatting information. | |
| Args: | |
| cb (Callable[[], str]): A lambda function that returns an f-string. Must be in the form | |
| lambda: f"string with {interpolations}"; Won't be called but parsed for its AST. | |
| Returns: | |
| Template: A Template object containing a tuple of string literals and Interpolation objects. | |
| Each Interpolation object contains: | |
| - value: The evaluated value of the expression | |
| - expr: The string representation of the expression | |
| - conv: The conversion flag (s, r, or a) if specified | |
| - format_spec: The format specification if specified | |
| Raises: | |
| ValueError: If any of these conditions are met: | |
| - t() is not called as a function (executing's edge case) | |
| - t() is not called with a lambda | |
| - The lambda does not return an f-string | |
| - The f-string contains unsupported elements | |
| Example: | |
| >>> x = 42 | |
| >>> t(lambda: f"Value is {x:d}") | |
| Template(("Value is ", Interpolation(value=42, expr="x", conv=None, format_spec="d"))) | |
| """ | |
| frame = sys._getframe(1) | |
| node = Source.executing(frame).node | |
| if not isinstance(node, ast.Call): | |
| raise ValueError("t() must be called as a function") | |
| arg0 = node.args[0] | |
| if not isinstance(arg0, ast.Lambda): | |
| raise ValueError("t() must be called with a lambda") | |
| if not isinstance(arg0.body, ast.JoinedStr): | |
| raise ValueError("t() lambda must return a f-string") | |
| values = [] | |
| for v in arg0.body.values: | |
| if isinstance(v, ast.Constant): | |
| values.append(v.value) | |
| elif isinstance(v, ast.FormattedValue): | |
| conv = _CONVERSIONS[v.conversion] | |
| expr = ast.unparse(v.value) | |
| val = eval(expr, frame.f_locals, frame.f_globals) | |
| spec = v.format_spec.values[0].value if v.format_spec is not None else "" # type: ignore | |
| values.append( | |
| Interpolation(value=val, expr=expr, conv=conv, format_spec=spec) | |
| ) | |
| else: | |
| raise ValueError("Unsupported f-string element") | |
| return Template(tuple(values)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment