Skip to content

feat(py)!: replace Rust handlebarrz with pure-Python dotpromptz-handlebars - #620

Merged
huangjeff5 merged 55 commits into
mainfrom
feat/handlebars-python
Oct 5, 2026
Merged

huangjeff5 merged 55 commits into
mainfrom
feat/handlebars-python

Conversation

@huangjeff5

@huangjeff5 huangjeff5 commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Replaces the Rust handlebarrz extension with a pure-Python dotpromptz-handlebars package (imported as dotpromptz_handlebars).

# before
from handlebarrz import Handlebars

hb = Handlebars()
# (Required Rust toolchain, sccache, and compiled binary wheels)

# after
from dotpromptz_handlebars import Handlebars

hb = Handlebars()
render = hb.compile('Hello {{user.name}}!')
output = render({'user': {'name': 'Alice'}})
# => Hello Alice!

Decisions

  • Already-published dotpromptz 0.1.x still imports handlebarrz: pip install dotpromptz==0.1.6 depends on dotpromptz-handlebars>=0.1.8 without an upper bound, and those releases do from handlebarrz import Handlebars. The 0.2.0 wheel still includes that module so legacy installs keep working. Remove this compatibility shim in a future stable release (e.g. 1.0.0) after users have had sufficient time to upgrade to dotpromptz>=0.2.0.
  • {} enters {{#if}} and {{#with}}: In Handlebars.js, {} is truthy and enters conditional blocks, while None, False, 0, "", and [] take else. We chose JS truthiness because a Dotprompt template must produce byte-for-byte identical output across TypeScript, Dart, and Python.
  • Spec fidelity over Python stringification idioms: Handlebars stringifies objects as [object Object], lists as 1,2, and floats like 1.0 as 1. We preserve this behavior so prompt rendering is portable across SDKs; structured data in prompts should use {{json var}}.
  • Context callables raise ValueError: Context payloads cannot supply executable callables. Dynamic logic must be explicitly registered via register_helper() to eliminate arbitrary template execution vulnerabilities.
  • The json helper raises TypeError: Non-serializable inputs passed to {{json var}} now raise TypeError instead of ValueError, matching Python standard library json.dumps() semantics.
  • Workflow hardening in python.yml and rust.yml: Deleting python/handlebarrz removed the need for Rust toolchains in python.yml and eliminated the python/handlebarrz/** path trigger in rust.yml. Touching these workflows triggered the repository's mandatory zizmor scanner, which blocks CI on unpinned action tags and known vulnerabilities (tj-actions/changed-files). Both workflows were hardened with immutable commit SHA action pins and least-privilege permissions so CI remains green.
  • Clippy pedantic lint in completions.rs: The workspace enables -D clippy::pedantic. Newer Rust toolchains in GitHub Actions enforce clippy::assert_is_empty, which fails CI on assert!(!output.is_empty()). Updating the test assertion to assert_ne!(output, Vec::<u8>::new()) was required to keep the workspace Clippy check green.

@github-actions github-actions Bot added documentation Improvements or additions to documentation python config labels Sep 25, 2026
@huangjeff5
huangjeff5 added this pull request to stack #622 September 25, 2026 20:09
@github-actions github-actions Bot added the bazel label Sep 25, 2026
@huangjeff5 huangjeff5 changed the title feat(py): add a pure-Python Handlebars package fix(py): render the Handlebars subset the way Handlebars 4.7.8 does Sep 30, 2026
@github-actions github-actions Bot added the js label Sep 30, 2026
@huangjeff5 huangjeff5 changed the title fix(py): render the Handlebars subset the way Handlebars 4.7.8 does feat(py): add a pure-Python Handlebars package Sep 30, 2026
@huangjeff5
huangjeff5 force-pushed the feat/handlebars-python branch from 55b9118 to 6cc9801 Compare September 30, 2026 19:07
@huangjeff5
huangjeff5 requested a review from pavelgj October 1, 2026 05:41

@pavelgj pavelgj left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments/questions

Comment thread python/handlebars/handlebars/_render.py Resolved
Comment thread python/handlebars/pyproject.toml Resolved
Comment thread .github/workflows/python.yml Resolved
Comment thread .github/workflows/python.yml Resolved
Comment thread python/pyproject.toml Resolved
@huangjeff5
huangjeff5 removed this pull request from stack #622 October 1, 2026 21:33
@huangjeff5
huangjeff5 added this pull request to stack #624 October 1, 2026 21:33
@github-actions github-actions Bot added dotpromptz root rust Pull requests that update rust code labels Oct 2, 2026
@huangjeff5
huangjeff5 force-pushed the feat/handlebars-python branch 12 times, most recently from c421f0c to 1465212 Compare October 2, 2026 04:14
Callers import dotpromptz_handlebars. The wheel name stays handlebars-dotprompt.
This PR still builds handlebarrz with Rust. PyPy runs on the PR that removes it.
Clears the medium and high advisories that fail the uv-lock-check security scan. PYSEC-2026-3472 (jupyter-server, low, no fix) stays below fail_on.
…bars

- Replace python/handlebarrz with pure Python python/handlebars
  (dotpromptz-handlebars 0.2.0, import dotpromptz_handlebars).
- Wire python/dotpromptz to dotpromptz-handlebars>=0.2.0.
- Drop Rust extension build, sccache, and toolchain from python.yml.
- Add PyPy 3.11 testing matrix cell in CI.
- Remove leftover Rust scripts, docs, and references across the repo.

BREAKING CHANGE: The `json` helper now raises `TypeError` instead of `ValueError` when given non-serializable objects, aligning with Python standard library `json.dumps()` semantics.
@huangjeff5
huangjeff5 force-pushed the feat/handlebars-python branch from af28125 to 0e4ac4c Compare October 5, 2026 16:23
Regular PRs checked out with secrets.RELEASE_PLEASE_TOKEN || github.token.
The fallback only applies when the secret is empty, so an expired PAT broke
checkout on every PR. Scope the PAT to release-please-- branches, which are
the only ones that push lockfile updates back.
Touching a workflow makes the Actions scan audit the whole file. Pin the three
floating actions in bazel.yml to the SHAs their tags resolve to today (no
behavior change). Revert the go.yml/js.yml edits: they pulled in pre-existing
Go/JS/zizmor failures unrelated to this stack.
@huangjeff5
huangjeff5 merged commit c05f2e7 into main Oct 5, 2026
73 checks passed
@huangjeff5
huangjeff5 deleted the feat/handlebars-python branch October 5, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bazel config documentation Improvements or additions to documentation dotpromptz js promptly python root rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants