Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
abidlabs committed Feb 26, 2025
1 parent 206231e commit 70bdfe9
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 31 deletions.
34 changes: 18 additions & 16 deletions groovy/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ def transpile(fn: Callable, validate: bool = False) -> str:
raise TranspilerError(
message=f"Function must take no arguments for client-side use, but got: {param_names}"
)

try:
source = inspect.getsource(fn)
source = textwrap.dedent(source)
Expand All @@ -545,27 +545,27 @@ def transpile(fn: Callable, validate: bool = False) -> str:

if validate:
try:
import gradio
has_gradio = True
import gradio # noqa: F401
except ImportError:
has_gradio = False
raise TranspilerError(message="Gradio must be installed for validation.")

func_node = None
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef) and node.name == fn.__name__:
func_node = node
break

if func_node:
return_nodes = []
for node in ast.walk(func_node):
if isinstance(node, ast.Return) and node.value is not None:
return_nodes.append(node)

if not return_nodes:
raise TranspilerError(message="Function must return Gradio component updates, but no return statement found.")

raise TranspilerError(
message="Function must return Gradio component updates, but no return statement found."
)

for return_node in return_nodes:
if not _is_valid_gradio_return(return_node.value):
line_no = return_node.lineno
Expand Down Expand Up @@ -609,38 +609,40 @@ def transpile(fn: Callable, validate: bool = False) -> str:
def _is_valid_gradio_return(node: ast.AST) -> bool:
"""
Check if a return value is a valid Gradio component or collection of components.
Args:
node: The AST node representing the return value
Returns:
bool: True if the return value is valid, False otherwise
"""
# Check for direct Gradio component call
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
if isinstance(node.func, ast.Attribute) and isinstance(
node.func.value, ast.Name
):
if node.func.value.id in {"gr", "gradio"}:
if node.args:
return False

for kw in node.keywords:
if kw.arg == "value":
return False
return True
elif isinstance(node.func, ast.Name):
if node.args:
return False

for kw in node.keywords:
if kw.arg == "value":
return False
return True

elif isinstance(node, (ast.Tuple, ast.List)):
if not node.elts:
return False
return all(_is_valid_gradio_return(elt) for elt in node.elts)

return False


Expand Down
2 changes: 1 addition & 1 deletion groovy/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.0
0.1.1
30 changes: 16 additions & 14 deletions tests/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,10 @@ def function_with_args(text_input):
def test_validate_non_gradio_return():
def invalid_return_function():
return "This is not a Gradio component"

with pytest.raises(TranspilerError) as e:
transpile(invalid_return_function, validate=True)

assert "Function must only return Gradio component updates" in str(e.value)


Expand All @@ -258,17 +258,19 @@ def mixed_return_function():
return gradio.Textbox(placeholder="Valid path")
else:
return "Invalid path"

with pytest.raises(TranspilerError) as e:
transpile(mixed_return_function, validate=True)

assert "Function must only return Gradio component updates" in str(e.value)


def test_validate_multiple_gradio_returns():
def multiple_components():
return gradio.Textbox(placeholder="Component 1"), gradio.Button(variant="primary")

return gradio.Textbox(placeholder="Component 1"), gradio.Button(
variant="primary"
)

result = transpile(multiple_components, validate=True)
expected = """function multiple_components() {
return [{"placeholder": "Component 1", "__type__": "update"}, {"variant": "primary", "__type__": "update"}];
Expand All @@ -279,7 +281,7 @@ def multiple_components():
def test_validate_no_value_param():
def valid_component():
return gradio.Textbox(placeholder="This is valid")

# This should pass validation
result = transpile(valid_component, validate=True)
expected = """function valid_component() {
Expand All @@ -291,28 +293,28 @@ def valid_component():
def test_validate_with_value_param():
def invalid_component():
return gradio.Textbox(value="This is invalid")

with pytest.raises(TranspilerError) as e:
transpile(invalid_component, validate=True)

assert "Function must only return Gradio component updates" in str(e.value)


def test_validate_with_positional_args():
def invalid_positional():
return gradio.Textbox("This is invalid")

with pytest.raises(TranspilerError) as e:
transpile(invalid_positional, validate=True)

assert "Function must only return Gradio component updates" in str(e.value)


def test_validate_mixed_valid_invalid_components():
def mixed_components():
return gradio.Textbox(placeholder="Valid"), gradio.Button(value="Invalid")

with pytest.raises(TranspilerError) as e:
transpile(mixed_components, validate=True)
assert "Function must only return Gradio component updates" in str(e.value)

assert "Function must only return Gradio component updates" in str(e.value)

0 comments on commit 70bdfe9

Please sign in to comment.