Skip to content

Commit

Permalink
feat(protocol-designer): add python field to commands and timeline (#…
Browse files Browse the repository at this point in the history
…17383)

# Overview

This starts the plumbing for Python generation from Protocol Designer.
AUTH-1385

We're adding a field for Python code to each command
(`CommandsAndWarnings`) and to each entry of the timeline
(`CommandsAndRobotState`). And in the reducer, we concatenate the Python
commands together, analogously to how we concatenate the JSON commands
together.

## Test Plan and Hands on Testing

I added unit tests to show that the Python code concatenation works, and
that the Python code gets copied from the CommandCreators to the
Timeline. The tests also demonstrate that the changes do NOT affect the
behavior of existing commands that don't generate Python.

I've also done hands-on testing in a private experimental branch that
has a more complete implementation of Python generation.

## Review requests

My first time making a functional change to PD, let me know if I'm
overlooking anything.

## Risk assessment

Low. Should not cause any observable change to PD's behavior.
  • Loading branch information
ddcc4 authored Jan 30, 2025
1 parent 253c300 commit a79e873
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 0 deletions.
74 changes: 74 additions & 0 deletions step-generation/src/__tests__/glue.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,30 @@ const divideCreator: any = (
}
}

const pythonHelloWorldCreator: any = (
params: CountParams,
invariantContext: InvariantContext,
prevState: CountState
) => {
return {
commands: [],
warnings: [],
python: 'print("Hello world")',
}
}

const pythonGoodbyeWorldCreator: any = (
params: CountParams,
invariantContext: InvariantContext,
prevState: CountState
) => {
return {
commands: [],
warnings: [],
python: 'print("Goodbye world")',
}
}

function mockNextRobotStateAndWarningsSingleCommand(
command: CountCommand,
invariantContext: any,
Expand Down Expand Up @@ -177,6 +201,9 @@ describe('reduceCommandCreators', () => {
{ command: 'multiply', params: { value: 2 } },
],
warnings: [],
// Note no `python` field here.
// Existing CommandCreators that don't emit Python should behave exactly the same as before.
// This test makes sure we do NOT produce results like `python:'undefined'` or `python:''` or `python:'\n'`.
})
})

Expand Down Expand Up @@ -226,6 +253,43 @@ describe('reduceCommandCreators', () => {
],
})
})

it('Python commands are joined together', () => {
const initialState: any = {}
const result: any = reduceCommandCreators(
[
curryCommandCreator(pythonHelloWorldCreator, {}),
curryCommandCreator(pythonGoodbyeWorldCreator, {}),
],
invariantContext,
initialState
)

expect(result).toEqual({
commands: [],
warnings: [],
python: 'print("Hello world")\nprint("Goodbye world")',
})
})

it('Python commands mixed with non-Python commands', () => {
const initialState: any = {}
const result: any = reduceCommandCreators(
[
curryCommandCreator(addCreator, { value: 1 }),
curryCommandCreator(pythonHelloWorldCreator, {}),
],
invariantContext,
initialState
)

expect(result).toEqual({
commands: [{ command: 'add', params: { value: 1 } }],
warnings: [],
python: 'print("Hello world")',
// should only get 1 line of Python with no stray newlines or `undefined`s.
})
})
})

describe('commandCreatorsTimeline', () => {
Expand All @@ -236,6 +300,7 @@ describe('commandCreatorsTimeline', () => {
curryCommandCreator(addCreatorWithWarning, { value: 4 }),
curryCommandCreator(divideCreator, { value: 0 }),
curryCommandCreator(multiplyCreator, { value: 3 }),
curryCommandCreator(pythonHelloWorldCreator, {}),
],
invariantContext,
initialState
Expand Down Expand Up @@ -263,6 +328,7 @@ describe('commandCreatorsTimeline', () => {
],
},
// no more steps in the timeline, stopped by error
// python output is suppressed too
],
})
})
Expand All @@ -275,6 +341,7 @@ describe('commandCreatorsTimeline', () => {
curryCommandCreator(addCreatorWithWarning, { value: 3 }),
curryCommandCreator(multiplyCreator, { value: 2 }),
curryCommandCreator(addCreatorWithWarning, { value: 1 }),
curryCommandCreator(pythonHelloWorldCreator, {}),
],
invariantContext,
initialState
Expand Down Expand Up @@ -309,6 +376,13 @@ describe('commandCreatorsTimeline', () => {
},
],
},
// Python hello world
{
robotState: { count: 17 },
commands: [],
warnings: [],
python: 'print("Hello world")',
},
])
})
})
2 changes: 2 additions & 0 deletions step-generation/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ export interface CommandsAndRobotState {
commands: CreateCommand[]
robotState: RobotState
warnings?: CommandCreatorWarning[]
python?: string
}

export interface CommandCreatorErrorResponse {
Expand All @@ -629,6 +630,7 @@ export interface CommandCreatorErrorResponse {
export interface CommandsAndWarnings {
commands: CreateCommand[]
warnings?: CommandCreatorWarning[]
python?: string
}
export type CommandCreatorResult =
| CommandsAndWarnings
Expand Down
1 change: 1 addition & 0 deletions step-generation/src/utils/commandCreatorsTimeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const commandCreatorsTimeline = (
commands: commandCreatorResult.commands,
robotState: nextRobotStateAndWarnings.robotState,
warnings: commandCreatorResult.warnings,
python: commandCreatorResult.python,
}
return {
timeline: [...acc.timeline, nextResult],
Expand Down
7 changes: 7 additions & 0 deletions step-generation/src/utils/reduceCommandCreators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface CCReducerAcc {
commands: CreateCommand[]
errors: CommandCreatorError[]
warnings: CommandCreatorWarning[]
python?: string
}
export const reduceCommandCreators = (
commandCreators: CurriedCommandCreator[],
Expand All @@ -36,6 +37,10 @@ export const reduceCommandCreators = (
}
}
const allCommands = [...prev.commands, ...next.commands]
const allPython = [
...(prev.python ? [prev.python] : []),
...(next.python ? [next.python] : []),
].join('\n')
const updates = getNextRobotStateAndWarnings(
next.commands,
invariantContext,
Expand All @@ -50,6 +55,7 @@ export const reduceCommandCreators = (
...(next.warnings || []),
...updates.warnings,
],
...(allPython && { python: allPython }),
}
},
{
Expand All @@ -69,5 +75,6 @@ export const reduceCommandCreators = (
return {
commands: result.commands,
warnings: result.warnings,
...(result.python && { python: result.python }),
}
}

0 comments on commit a79e873

Please sign in to comment.