Problem
Many workflows need data transformation logic that's too complex for JavaScript code steps but doesn't warrant a separate service. Today, the only option is embedding Python inside bash steps:
{
"id": "analyzeData",
"type": "bash",
"bash": {
"command": "python3 -c \"\nimport json, sys\nfrom datetime import datetime, timedelta\n\ndays = {{$.input.days}}\ncutoff = (datetime.now() - timedelta(days=days)).isoformat()\n\nruns = []\ntry:\n with open('data.jsonl') as f:\n for line in f:\n try:\n r = json.loads(line.strip())\n if r.get('ts', '') >= cutoff:\n runs.append(r)\n except: pass\nexcept: pass\n\nsectors = {}\nfor r in runs:\n if r.get('sector'):\n sectors[r['sector']] = sectors.get(r['sector'], 0) + 1\n\nprint(json.dumps({'count': len(runs), 'sectors': sectors}))\n\"",
"timeout": 15000,
"parseJson": true
}
}
This pattern appears in 6+ flows in our codebase, with embedded Python ranging from 40-100+ lines.
Problems with bash-wrapped Python
- Escaping hell — double quotes must be escaped as
\", template interpolation {{...}} inside Python strings is fragile, triple-quotes (''') can be injected if interpolated values contain them
- No syntax validation — Python syntax errors only surface at runtime; static validators can't easily parse Python from inside JSON strings
- No line numbers — when Python errors occur, the traceback references
python3 -c "<string>" with no useful line numbers
- Template interpolation risks —
{{$.input.companyName}} injected into Python code breaks if the value contains quotes or special characters
- IDE support — zero syntax highlighting, no autocomplete, no linting for embedded Python
Proposal
Add a native Python step type:
{
"id": "analyzeData",
"type": "python",
"python": {
"source": "import json\nfrom datetime import datetime, timedelta\n\ndays = inputs['days']\n...\nreturn {'count': len(runs), 'sectors': sectors}",
"module": "steps/analyze_data.py",
"timeout": 15000
}
}
Where:
source is inline Python (like code steps today)
module references an external .py file (like code step modules)
inputs dict provides access to $.input.*
steps dict provides access to $.steps.*
- Return value becomes the step output (auto-serialized to JSON)
- Template interpolation happens on the inputs, not inside the Python source
Why this matters
Python is the natural language for data transformation, file parsing, API orchestration, and ML pipelines. Teams that use One CLI for these workflows are forced into the most fragile pattern in the platform. A native step type would:
- Eliminate escaping issues entirely
- Enable static validation (ast.parse on the source)
- Support external module files for better IDE tooling
- Separate template interpolation from code execution
Problem
Many workflows need data transformation logic that's too complex for JavaScript code steps but doesn't warrant a separate service. Today, the only option is embedding Python inside bash steps:
{ "id": "analyzeData", "type": "bash", "bash": { "command": "python3 -c \"\nimport json, sys\nfrom datetime import datetime, timedelta\n\ndays = {{$.input.days}}\ncutoff = (datetime.now() - timedelta(days=days)).isoformat()\n\nruns = []\ntry:\n with open('data.jsonl') as f:\n for line in f:\n try:\n r = json.loads(line.strip())\n if r.get('ts', '') >= cutoff:\n runs.append(r)\n except: pass\nexcept: pass\n\nsectors = {}\nfor r in runs:\n if r.get('sector'):\n sectors[r['sector']] = sectors.get(r['sector'], 0) + 1\n\nprint(json.dumps({'count': len(runs), 'sectors': sectors}))\n\"", "timeout": 15000, "parseJson": true } }This pattern appears in 6+ flows in our codebase, with embedded Python ranging from 40-100+ lines.
Problems with bash-wrapped Python
\", template interpolation{{...}}inside Python strings is fragile, triple-quotes (''') can be injected if interpolated values contain thempython3 -c "<string>"with no useful line numbers{{$.input.companyName}}injected into Python code breaks if the value contains quotes or special charactersProposal
Add a native Python step type:
{ "id": "analyzeData", "type": "python", "python": { "source": "import json\nfrom datetime import datetime, timedelta\n\ndays = inputs['days']\n...\nreturn {'count': len(runs), 'sectors': sectors}", "module": "steps/analyze_data.py", "timeout": 15000 } }Where:
sourceis inline Python (like code steps today)modulereferences an external.pyfile (like code step modules)inputsdict provides access to$.input.*stepsdict provides access to$.steps.*Why this matters
Python is the natural language for data transformation, file parsing, API orchestration, and ML pipelines. Teams that use One CLI for these workflows are forced into the most fragile pattern in the platform. A native step type would: