[Issue #799] Transform PoC#810
Conversation
| def to_common(native: Any) -> TransformResult[Any]: | ||
| try: | ||
| result = transform_from_mapping(native, to_common_mapping, handlers=merged) | ||
| # TODO (full SDK): run model_validate on result and append validation |
There was a problem hiding this comment.
I'm not sure if we should handle this TODO here or later down the road. Would love thoughts on this.
|
🚀 Website Preview Deployed! Preview your changes at: https://cg-pr-810.billy-daly.workers.dev This preview will be automatically deleted when the PR is closed. |
| to_common_mapping: dict[str, Any], | ||
| from_common_mapping: dict[str, Any], | ||
| handlers: dict[str, Handler] | None = None, | ||
| common_model: type[BaseModel] | None = None, |
There was a problem hiding this comment.
Calling this out as a needed change from the existing ADR. Without having the common_model in the signature we don't have a way to validate an extended class that contains optional fields. @SnowboardTechie for awareness
There was a problem hiding this comment.
Nice work on round 2. 7 of my 9 earlier findings are fully addressed, and the other 2 are documented-as-intended. The HandlerError refactor in particular reads much cleaner than the hardcoded handler=None we started with.
One latent bug worth surfacing before this ships: transform_node silently treats handler keys as literals when a non-handler sibling iterates first. The fact that build_transforms and transform_from_mapping are both public surface now makes it more load-bearing than before. Reproducer in the inline on utils/transformation.py.
Two small follow-ups on the round-2 fix itself, both flagged on individual threads: HandlerError is a behavior change for dump_with_mapping / validate_with_mapping callers (worth deciding whether to make it ValueError-compatible), and the new attribution path is not covered by an assertion in the exception test.
SnowboardTechie
left a comment
There was a problem hiding this comment.
Round-3 looks great! The ADR has some drift against #803 in the merge_extensions / filters area, but IMO we should rebase main onto current HOLD-transforms after this merges.
Approving w/ 2 small suggests: git rm lib/python-sdk/.coverage and add .coverage to either lib/python-sdk/.gitignore or the root. It looks like Pytest-cov SQLite output landed in the tree. And another trimming an extra /.
There was a problem hiding this comment.
Not sure we want this commited?
Co-authored-by: Bryan Thompson <18094023+SnowboardTechie@users.noreply.github.com>
Summary
Changes proposed
Implements a bidirectional transform framework as a proof-of-concept for the plugin system. Plugin authors can now define declarative mapping dicts that compile into
to_common/from_commoncallables, enabling data translation between a source system's native format and the CommonGrants format without writing imperative transform code.New utilities:
build_transforms()— compiles a pair of mapping dicts into typed(to_common, from_common)callables with structural validation at call timeTransformResult— unconditional return shape carrying aresultdict and a list ofPluginErrors (non-fatal; partial result always returned)PluginError— structured error type withpath,handler,source_value, andcausefields for programmatic error handlingObjectSchemasInput— bundlesto_common/from_commonfor a single object type; passed todefine_plugin()viatransform_schemasPluginMeta— optional metadata attached to a plugin (name,version,source_system,capabilities)Built-in mapping handlers (in
utils/transformation.py):fieldconstmatchswitchmatch(backward compat)numberToStringstringToNumberCustom handlers can be registered per
build_transforms()call and cannot override built-in names.Sample grants.gov plugin (
examples/plugins/grants_gov/): demonstrates the full interface — bidirectional field mapping, status case conversion, funding amounts, key dates, andplugin metadata.
Documentation (
extensions/README.md): new "Bidirectional Transforms" section covering mapping format, handler reference, custom handlers, and roundtrip usage.Tests (
tests/extensions/,tests/utils/): coverage forbuild_transforms,TransformResult,PluginError,ObjectSchemasInput,PluginMeta, andtransform_from_mapping.Context for reviewers
This is a proof-of-concept scoped to the transform layer only. It is intentionally limited:
build_transforms()validates mapping structure at call time but cannot validate that field paths resolve against real data (deferred to full SDK with schema introspection).matchare not reversible.TODO (full SDK)comments intransforms.pycall out wheremodel_validateintegration belongs (indefine_plugin(), which knows the target Pydantic model).Verification:
Run the working example end-to-end: