ty_plugin_sdk
The authoring SDK for sandboxed ty-extended semantic extensions.
Use this crate to declare what an extension owns, implement typed semantic hooks, return
declarative patches, and export the implementation as a WebAssembly module. It re-exports all wire
types from ty_plugin_protocol as ty_plugin_sdk::protocol, so an extension normally has only one
ty dependency.
Architecture Boundary
An extension does not link to ty_python_semantic, Salsa, AST ids, or checker-owned type objects.
The host sends a serialized request for a manifest claim; the extension returns a serialized patch
that the host validates and applies.
ty semantic query -> typed protocol request -> extension hook -> declarative patch -> ty
The same Plugin implementation can be unit-tested natively and exported to WASM for production.
Quick Start
Create a library crate that produces both a native Rust library and a WASM-compatible dynamic library:
[]
= "my-ty-extension"
= "0.1.0"
= "2024"
[]
= ["rlib", "cdylib"]
[]
= "0.0.3"
Implement Plugin, claim the matching hook in the manifest, and export it:
use ;
use ;
;
export_plugin!;
Every hook has a PluginResponse::NoChange default. An implementation only overrides the hooks it
uses.
Build the WASM Module
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
The example produces
target/wasm32-unknown-unknown/release/my_ty_extension.wasm.
export_plugin! generates the ty_plugin_alloc and ty_plugin_handle exports expected by the
host. The ABI transports JSON through the module's linear memory; the extension has no WASI or
ambient host capabilities.
Manifests and Claims
ManifestBuilder starts with protocol defaults and keeps capability flags synchronized with
claims. For example, claim_call_return adds a function claim and enables the call-return
capability.
Claims keep routing precise: ty invokes an extension only for the classes, functions, methods, attributes, settings, or mutations it declared. Useful builder methods include:
claim_class_transformandclaim_subclass_transform;claim_class_member,claim_instance_member, and subclass member claims;claim_call_signatureandclaim_call_return, including method variants;project_index, settings claims, and cross-symbol contribution targets;claim_mutationsandclaim_mutations_on_subclass;stub_overlay,config_schema, anddefault_config.
Always set a narrow ty_compatibility range for a published extension. The protocol and SDK are
versioned independently from ty-extended.
Hook Reference
Plugin method |
Capability | Typical response |
|---|---|---|
analyze_class |
class-transform |
ClassPatch |
resolve_class_member |
class-member |
MemberPatch |
resolve_instance_member |
instance-member |
MemberPatch |
adjust_call_signature |
call-signature |
CallSignaturePatch |
adjust_call_return |
call-return |
CallReturnPatch |
build_project_index |
project-index |
ProjectIndexResponse |
additional_dependencies |
additional-dependencies |
Vec<PluginDependency> |
validate_mutation |
mutation-validation |
MutationResponse |
The dsl module provides small constructors for fields, parameters, signatures, members, class
patches, and call responses. For shapes not covered by a helper, use the re-exported protocol types
directly.
Type Expressions
Types cross the boundary as TypeExpr, never as checker internals:
use TypeExpr;
let annotation = annotation;
let expression = expression;
assert_eq!;
assert_eq!;
Choose annotation mode for type syntax, expression mode for runtime symbol expressions, and stub mode for a complete generated stub declaration.
Test an Extension
Test the same implementation at three levels:
- Call hook methods directly in ordinary Rust unit tests.
- Call
Plugin::handleorPlugin::handle_jsonto verify dispatch and wire serialization. - Build
wasm32-unknown-unknownand run the packaged artifact with ty-extended against a fixture Python project.
Native tests stay fast because export_plugin! expands to nothing outside wasm32.
Package and Configure It
Ship the generated .wasm file with a JSON serialization of the same PluginManifest. A Python
library can place both files in its import package and name the manifest ty-plugin.json; users
then opt into installed-package discovery:
# ty.toml
[]
= true
For an explicitly managed artifact:
# ty.toml
[]
= true
[[]]
= "my-extension"
= ".ty/plugins/my_extension.wasm"
= "wasm"
= ".ty/plugins/my-extension.plugin.json"
= true
See the ty-extended extension authoring guide for the end-to-end packaging workflow and the runtime guide for host loading, sandboxing, and failure behavior.