|
|
import os |
|
|
import numpy as np |
|
|
import subprocess |
|
|
import re |
|
|
import copy |
|
|
from tasksolver.exceptions import CodeExecutionException, ToolCallException |
|
|
from pathlib import Path |
|
|
|
|
|
def edit_code(code_str:str, code_before:str, code_after:str): |
|
|
""" |
|
|
Args: |
|
|
code_str: a string containing multiple lines of python code. |
|
|
code_before: a string containing lines that should be replaced by code in `code_after` |
|
|
code_after: a string containing lines that replace the code in `code_before` |
|
|
Returns: |
|
|
modified_code_str: a string containing the modified code. |
|
|
Raises: |
|
|
ToolCallException when the code replacement/editing cannot be done. |
|
|
""" |
|
|
code_before = code_before.strip() |
|
|
code_after = code_after.strip() |
|
|
|
|
|
start_idx = code_str.find(code_before) |
|
|
if start_idx == -1: |
|
|
raise ToolCallException("Code to replace not found") |
|
|
|
|
|
|
|
|
end_idx = start_idx + len(code_before) |
|
|
modified_code_str = code_str[:start_idx] + code_after + code_str[end_idx:] |
|
|
return modified_code_str |
|
|
|
|
|
|
|
|
def add_line_numbers(code_string:str, delimiter:str="|"): |
|
|
""" |
|
|
Args: |
|
|
code_string: code string |
|
|
delimiter: the symbol that separates the line number and the line of code. |
|
|
e.g. "|" leads to '10| print("hello world")' |
|
|
""" |
|
|
numbered_code_string = "\n".join([str(line_number+1)+delimiter+" "+el for line_number, el in enumerate(code_string.split("\n"))]) |
|
|
return numbered_code_string |
|
|
|
|
|
|
|
|
def get_code_as_string(script_path): |
|
|
with open(script_path, 'r') as f: |
|
|
code_str = f.read() |
|
|
return code_str |
|
|
|
|
|
|
|
|
def blenderai_uniform_sample(low:float, high:float, num_samples:int): |
|
|
""" |
|
|
Args: |
|
|
low: lower bound of the range being sampled from |
|
|
high: higher bound of the range being sampled from |
|
|
num_samples: Number of samples in that range. Should be positive. |
|
|
""" |
|
|
assert low <= high |
|
|
assert num_samples > 0 |
|
|
return np.linspace(low, high, num_samples) |
|
|
|
|
|
|
|
|
def get_macroed_code(code_str): |
|
|
uniform_sample_regex = r'blenderai_uniform_sample\s*\([^)]*\)' |
|
|
instances = re.findall(uniform_sample_regex, code_str) |
|
|
|
|
|
|
|
|
if len(instances) == 0: |
|
|
return [code_str] |
|
|
|
|
|
ranges = [] |
|
|
for ins in instances: |
|
|
try: |
|
|
options = eval(ins) |
|
|
except: |
|
|
raise CodeExecutionException |
|
|
ranges.append(options) |
|
|
|
|
|
parameter_space = np.stack ([el.flatten() for el in np.meshgrid(*ranges)]) |
|
|
parameter_space = parameter_space.transpose() |
|
|
|
|
|
def replace_matches_with_list(input_string, pattern, replacements): |
|
|
def replace(match): |
|
|
return replacements.pop(0) |
|
|
return re.sub(pattern, replace, input_string) |
|
|
|
|
|
code_instances = [] |
|
|
for param_instance in parameter_space: |
|
|
string_params = [str(el) for el in param_instance] |
|
|
code_instance = replace_matches_with_list(code_str, uniform_sample_regex, string_params) |
|
|
code_instances.append(code_instance) |
|
|
|
|
|
return code_instances |
|
|
|
|
|
|
|
|
def get_macroed_code_as_string(script_path): |
|
|
with open(script_path , 'r') as f: |
|
|
code_str = f.read() |
|
|
return get_macroed_code(code_str) |
|
|
|
|
|
|
|
|
def get_code_diffs(before:Path, after: Path): |
|
|
""" |
|
|
Args: |
|
|
before: path to before |
|
|
after: path to after |
|
|
Returns: |
|
|
list of tuples, (change_type, additional_info_dictionary) |
|
|
""" |
|
|
assert os.path.exists(str(before)) and os.path.exists(str(after)) |
|
|
|
|
|
completed_process = subprocess.run(["diff", str(before), str(after)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
|
|
output_str = completed_process.stdout.decode("utf-8") |
|
|
|
|
|
changes = [] |
|
|
|
|
|
mode = None |
|
|
data = {} |
|
|
|
|
|
for line in output_str.split("\n"): |
|
|
if len(line.strip()) > 0: |
|
|
|
|
|
if not (line.startswith(">") or line.startswith("<") or line.startswith("---")): |
|
|
if mode is not None: |
|
|
changes.append((mode, data)) |
|
|
|
|
|
if "a" in line: |
|
|
mode = "add" |
|
|
data = {"added_lines":[]} |
|
|
|
|
|
elif "c" in line: |
|
|
mode = "change" |
|
|
data = {"deleted_lines": [], "added_lines": []} |
|
|
|
|
|
elif "d" in line: |
|
|
mode = "delete" |
|
|
data = {"deleted_lines": []} |
|
|
else: |
|
|
raise ValueError(f"Couldn't determine the mode of {line}") |
|
|
else: |
|
|
assert mode is not None, f"mode not set. Something wrong with the output:\n{output_str}" |
|
|
|
|
|
if mode == "change": |
|
|
if line.startswith("<"): |
|
|
deleted = line[1:].strip() |
|
|
data["deleted_lines"].append(deleted) |
|
|
|
|
|
elif line.startswith(">"): |
|
|
added = line[1:].strip() |
|
|
data["added_lines"].append(added) |
|
|
elif line.startswith("---"): |
|
|
pass |
|
|
else: |
|
|
raise ValueError(f"unexpected line in mode {mode}: {line}") |
|
|
|
|
|
if mode == "delete": |
|
|
if line.startswith("<"): |
|
|
deleted = line[1:].strip() |
|
|
data ["deleted_lines"].append(deleted) |
|
|
else: |
|
|
raise ValueError(f"unexpected line in mode {mode}: {line}") |
|
|
|
|
|
if mode == "add": |
|
|
if line.startswith(">"): |
|
|
added = line[1:].strip() |
|
|
data["added_lines"].append(added) |
|
|
else: |
|
|
raise ValueError(f"unexpected line in mode {mode}: {line}") |
|
|
|
|
|
|
|
|
if mode is not None: |
|
|
changes.append((mode, data)) |
|
|
|
|
|
return changes |
|
|
|
|
|
def tally_total_changes(changes_list): |
|
|
num_adds = 0 |
|
|
num_dels = 0 |
|
|
num_changes = 0 |
|
|
|
|
|
num_added_lines = 0 |
|
|
num_added_chars = 0 |
|
|
num_deleted_lines = 0 |
|
|
num_deleted_chars = 0 |
|
|
|
|
|
for cl in changes_list: |
|
|
if cl[0] == 'add': |
|
|
num_adds += 1 |
|
|
elif cl[0] == "delete": |
|
|
num_dels += 1 |
|
|
elif cl[0] == "change": |
|
|
num_changes += 1 |
|
|
|
|
|
if cl[0] in ("add", "change"): |
|
|
num_added_lines += len(cl[1]["added_lines"]) |
|
|
num_added_chars += sum([len(el.strip()) for el in cl[1]["added_lines"]]) |
|
|
|
|
|
if cl[0] in ("delete", "change"): |
|
|
num_deleted_lines += len(cl[1]["deleted_lines"]) |
|
|
num_deleted_chars += sum([len(el.strip()) for el in cl[1]["deleted_lines"]]) |
|
|
|
|
|
return {"num_adds": num_adds, "num_dels": num_dels, "num_changes": num_changes, |
|
|
"num_added_lines": num_added_lines, "num_added_chars": num_added_chars, |
|
|
"num_deleted_lines": num_deleted_lines, "num_deleted_chars": num_deleted_chars |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
code = """ |
|
|
a = blenderai_uniform_sample (9, 12, 2) |
|
|
b = blenderai_uniform_sample (-1, 2, 3) |
|
|
c = a + b |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
out = get_macroed_code(code) |
|
|
import ipdb; ipdb.set_trace() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|