Skip to content

Commit d2d4b21

Browse files
committed
feat: add support for CodeBlocks with argv
Signed-off-by: Nick Mitchell <[email protected]>
1 parent 41f6429 commit d2d4b21

File tree

8 files changed

+186
-18
lines changed

8 files changed

+186
-18
lines changed

pdl-live-react/src/pdl_ast.d.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,32 @@ export type Code =
19471947
| ImportBlock
19481948
| ErrorBlock
19491949
| EmptyBlock
1950+
| (
1951+
| boolean
1952+
| number
1953+
| string
1954+
| FunctionBlock
1955+
| CallBlock
1956+
| LitellmModelBlock
1957+
| GraniteioModelBlock
1958+
| CodeBlock
1959+
| GetBlock
1960+
| DataBlock
1961+
| IfBlock
1962+
| MatchBlock
1963+
| RepeatBlock
1964+
| TextBlock
1965+
| LastOfBlock
1966+
| ArrayBlock
1967+
| ObjectBlock
1968+
| MessageBlock
1969+
| ReadBlock
1970+
| IncludeBlock
1971+
| ImportBlock
1972+
| ErrorBlock
1973+
| EmptyBlock
1974+
| null
1975+
)[]
19501976
| null
19511977
/**
19521978
* Name of the variable used to store the result of the execution of the block.

pdl-live-react/src/pdl_ast_utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function map_block_children(
5151
}
5252
})
5353
.with({ kind: "code" }, (block) => {
54-
const code = f(block.code)
54+
const code = Array.isArray(block.code) ? block.code.map(f) : f(block.code)
5555
return { ...block, code: code }
5656
})
5757
.with({ kind: "get" }, (block) => block)
@@ -155,7 +155,11 @@ export function iter_block_children(
155155
if (block.input) f(block.input)
156156
})
157157
.with({ kind: "code" }, (block) => {
158-
f(block.code)
158+
if (Array.isArray(block.code)) {
159+
block.code.forEach(f)
160+
} else {
161+
f(block.code)
162+
}
159163
})
160164
.with({ kind: "get" }, () => {})
161165
.with({ kind: "data" }, () => {})

src/pdl/pdl-schema.json

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,88 @@
13801380
{
13811381
"$ref": "#/$defs/EmptyBlock"
13821382
},
1383+
{
1384+
"items": {
1385+
"anyOf": [
1386+
{
1387+
"type": "boolean"
1388+
},
1389+
{
1390+
"type": "integer"
1391+
},
1392+
{
1393+
"type": "number"
1394+
},
1395+
{
1396+
"type": "string"
1397+
},
1398+
{
1399+
"$ref": "#/$defs/FunctionBlock"
1400+
},
1401+
{
1402+
"$ref": "#/$defs/CallBlock"
1403+
},
1404+
{
1405+
"$ref": "#/$defs/LitellmModelBlock"
1406+
},
1407+
{
1408+
"$ref": "#/$defs/GraniteioModelBlock"
1409+
},
1410+
{
1411+
"$ref": "#/$defs/CodeBlock"
1412+
},
1413+
{
1414+
"$ref": "#/$defs/GetBlock"
1415+
},
1416+
{
1417+
"$ref": "#/$defs/DataBlock"
1418+
},
1419+
{
1420+
"$ref": "#/$defs/IfBlock"
1421+
},
1422+
{
1423+
"$ref": "#/$defs/MatchBlock"
1424+
},
1425+
{
1426+
"$ref": "#/$defs/RepeatBlock"
1427+
},
1428+
{
1429+
"$ref": "#/$defs/TextBlock"
1430+
},
1431+
{
1432+
"$ref": "#/$defs/LastOfBlock"
1433+
},
1434+
{
1435+
"$ref": "#/$defs/ArrayBlock"
1436+
},
1437+
{
1438+
"$ref": "#/$defs/ObjectBlock"
1439+
},
1440+
{
1441+
"$ref": "#/$defs/MessageBlock"
1442+
},
1443+
{
1444+
"$ref": "#/$defs/ReadBlock"
1445+
},
1446+
{
1447+
"$ref": "#/$defs/IncludeBlock"
1448+
},
1449+
{
1450+
"$ref": "#/$defs/ImportBlock"
1451+
},
1452+
{
1453+
"$ref": "#/$defs/ErrorBlock"
1454+
},
1455+
{
1456+
"$ref": "#/$defs/EmptyBlock"
1457+
},
1458+
{
1459+
"type": "null"
1460+
}
1461+
]
1462+
},
1463+
"type": "array"
1464+
},
13831465
{
13841466
"type": "null"
13851467
}

src/pdl/pdl_ast.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@
1515
Union,
1616
)
1717

18-
from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, RootModel
18+
from pydantic import (
19+
BaseModel,
20+
BeforeValidator,
21+
ConfigDict,
22+
Field,
23+
RootModel,
24+
model_validator,
25+
)
1926
from pydantic.json_schema import SkipJsonSchema
2027

2128
from .pdl_lazy import PdlDict, PdlLazy
@@ -462,10 +469,16 @@ class CodeBlock(LeafBlock):
462469
]
463470
"""Programming language of the code.
464471
"""
465-
code: "BlockType"
472+
code: "BlockOrBlocksType"
466473
"""Code to execute.
467474
"""
468475

476+
@model_validator(mode="after")
477+
def lang_is_python(self):
478+
if isinstance(self.code, list) and self.lang != "command":
479+
raise ValueError("CodeBlock code field is array for non-command block")
480+
return self
481+
469482

470483
class GetBlock(LeafBlock):
471484
"""

src/pdl/pdl_ast_utils.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ def iter_block_children(f: Callable[[BlockType], None], block: BlockType) -> Non
4848
if block.pdl__trace is not None:
4949
f(block.pdl__trace)
5050
case CodeBlock():
51-
f(block.code)
51+
if isinstance(block.code, list):
52+
for b in block.code:
53+
f(b)
54+
else:
55+
f(block.code)
5256
case GetBlock():
5357
pass
5458
case DataBlock():
@@ -150,7 +154,10 @@ def map_block_children(f: MappedFunctions, block: BlockType) -> BlockType:
150154
if block.parameters is not None:
151155
block.parameters = f.f_expr(block.parameters)
152156
case CodeBlock():
153-
block.code = f.f_block(block.code)
157+
if isinstance(block.code, list):
158+
block.code = [f.f_block(b) for b in block.code]
159+
else:
160+
block.code = f.f_block(block.code)
154161
case GetBlock():
155162
pass
156163
case DataBlock():

src/pdl/pdl_dumper.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ def block_to_dict( # noqa: C901
135135
d["modelResponse"] = block.modelResponse
136136
case CodeBlock():
137137
d["lang"] = block.lang
138-
d["code"] = block_to_dict(block.code, json_compatible)
138+
if isinstance(block.code, list):
139+
d["code"] = [block_to_dict(b, json_compatible) for b in block.code]
140+
else:
141+
d["code"] = block_to_dict(block.code, json_compatible)
139142
case GetBlock():
140143
d["get"] = block.get
141144
case DataBlock():

src/pdl/pdl_interpreter.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,14 +1431,24 @@ def process_call_code(
14311431
state: InterpreterState, scope: ScopeType, block: CodeBlock, loc: PdlLocationType
14321432
) -> tuple[PdlLazy[Any], LazyMessages, ScopeType, CodeBlock]:
14331433
background: LazyMessages
1434-
code_, _, _, block = process_block_of(
1435-
block,
1436-
"code",
1437-
state.with_yield_result(False).with_yield_background(False),
1438-
scope,
1439-
loc,
1440-
)
1441-
code_s = code_.result()
1434+
code_a = None
1435+
if isinstance(block.code, list):
1436+
code_s = ""
1437+
code_a, _, _, _ = process_block(
1438+
state.with_yield_result(False).with_yield_background(False),
1439+
scope,
1440+
ArrayBlock(array=block.code),
1441+
loc,
1442+
)
1443+
else:
1444+
code_, _, _, block = process_block_of(
1445+
block,
1446+
"code",
1447+
state.with_yield_result(False).with_yield_background(False),
1448+
scope,
1449+
loc,
1450+
)
1451+
code_s = code_.result()
14421452
match block.lang:
14431453
case "python":
14441454
try:
@@ -1456,7 +1466,7 @@ def process_call_code(
14561466
) from exc
14571467
case "command":
14581468
try:
1459-
result = call_command(code_s)
1469+
result = call_command(code_s, code_a)
14601470
background = PdlList(
14611471
[
14621472
PdlDict( # type: ignore
@@ -1530,8 +1540,11 @@ def call_python(code: str, scope: ScopeType) -> PdlLazy[Any]:
15301540
return PdlConst(result)
15311541

15321542

1533-
def call_command(code: str) -> PdlLazy[str]:
1534-
args = shlex.split(code)
1543+
def call_command(code: str, code_a: PdlLazy[list[str]] | None) -> PdlLazy[str]:
1544+
if code_a is not None and isinstance(code_a.result(), list):
1545+
args = code_a.result()
1546+
else:
1547+
args = shlex.split(code)
15351548
p = subprocess.run(
15361549
args, capture_output=True, text=True, check=False, shell=False
15371550
) # nosec B603

tests/test_code.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ def test_contribute_false():
6161
]
6262
}
6363

64+
command_data_args = {
65+
"lastOf": [
66+
{
67+
"def": "world",
68+
"lang": "command",
69+
"code": ["echo", "-n", "World"],
70+
"contribute": [],
71+
},
72+
"Hello ${ world }!",
73+
]
74+
}
75+
6476

6577
def test_command():
6678
result = exec_dict(command_data, output="all")
@@ -70,6 +82,14 @@ def test_command():
7082
assert scope["world"] == "World"
7183

7284

85+
def test_command_args():
86+
result = exec_dict(command_data_args, output="all")
87+
document = result["result"]
88+
scope = result["scope"]
89+
assert document == "Hello World!"
90+
assert scope["world"] == "World"
91+
92+
7393
def test_jinja1():
7494
prog_str = """
7595
defs:

0 commit comments

Comments
 (0)