forked from cleveragents/cleveragents-core
82 lines
2.8 KiB
Python
82 lines
2.8 KiB
Python
"""Robot library providing helpers for inline Python scripts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
def fix_python_indentation(script: str) -> str:
|
|
"""Reconstruct indentation lost when Robot strips leading spaces.
|
|
|
|
Args:
|
|
script: Source code with indentation removed from the start of each line.
|
|
|
|
Returns:
|
|
Script with indentation restored so Python can execute it.
|
|
"""
|
|
lines = script.split("\n")
|
|
result: list[str] = []
|
|
indent_level = 0
|
|
block_stack: list[str] = []
|
|
|
|
for line in lines:
|
|
stripped = line.strip()
|
|
|
|
if not stripped:
|
|
result.append("")
|
|
continue
|
|
|
|
is_dedenting = stripped.startswith(
|
|
("else:", "elif ", "except:", "except ", "finally:")
|
|
)
|
|
|
|
# Check if we should dedent from a class/function body back to module level
|
|
# This happens when we see import, class, or a top-level statement after a class
|
|
is_module_level_statement = False
|
|
if indent_level > 0 and block_stack:
|
|
# If we're inside a class and see an import, we need to dedent
|
|
if stripped.startswith(("import ", "from ")) or (
|
|
stripped.startswith("class ") and block_stack[-1] == "class"
|
|
):
|
|
is_module_level_statement = True
|
|
|
|
if is_dedenting and block_stack:
|
|
if stripped.startswith(("except:", "except ", "finally:")):
|
|
dedent_levels = 0
|
|
for block in reversed(block_stack):
|
|
dedent_levels += 1
|
|
if block == "try":
|
|
break
|
|
indent_level = max(0, indent_level - dedent_levels)
|
|
else:
|
|
indent_level = max(0, indent_level - 1)
|
|
elif is_module_level_statement:
|
|
# Dedent back to module level
|
|
indent_level = 0
|
|
block_stack.clear()
|
|
|
|
current_indent = indent_level
|
|
result.append(" " * current_indent + stripped)
|
|
|
|
if stripped.startswith("try:"):
|
|
block_stack.append("try")
|
|
indent_level += 1
|
|
elif stripped.startswith(("except:", "except ", "finally:")):
|
|
while block_stack and block_stack[-1] != "try":
|
|
block_stack.pop()
|
|
if (
|
|
stripped.startswith("finally:")
|
|
and block_stack
|
|
and block_stack[-1] == "try"
|
|
):
|
|
block_stack.pop()
|
|
indent_level += 1
|
|
elif stripped.endswith(":") and not is_dedenting:
|
|
if stripped.startswith("class "):
|
|
block_stack.append("class")
|
|
else:
|
|
block_stack.append("other")
|
|
indent_level += 1
|
|
elif is_dedenting and stripped.endswith(":"):
|
|
indent_level += 1
|
|
|
|
return "\n".join(result)
|