(extension_checklist)=

Extension author checklist

The rules in this section, gathered into one list to run through before you publish. Each links to the page that explains it.

Protocol

  • [ ] Every getter's capsule name matches its method name. __datafusion_<thing>__ returns a capsule named datafusion_<thing>. → {ref}extension_capsule_protocol
  • [ ] Your getter does not inspect its argument. Pass it to ffi_logical_codec_from_pycapsule and move on. It is not always a session, and when it is, it is not the Python SessionContext wrapper. → {ref}extension_getter_argument
  • [ ] You never construct a SessionContext inside your library. Take what the FFI constructors need off the argument you were handed. A context built inline is already dropped by the time the capsule is used. → {ref}extension_getter_argument
  • [ ] You do not depend on the datafusion-python crate. → {ref}extension_why_ffi

Codecs

  • [ ] Your codec claims narrowly. Downcast to your own types. Claiming a broad category takes nodes from every library installed after you and makes your library order-sensitive for everyone downstream. → {ref}extension_codec_order
  • [ ] You declare __datafusion_codec_id__ if you might rename the class. The default id is the exporting class's import path, so a rename stops older plans decoding. → {ref}extension_codec_ids
  • [ ] Name-only decoders check name before trusting buf. An empty payload has no id to route on, so your try_decode_udf can be called with another library's function name and an empty buffer. → {ref}extension_codecs
  • [ ] You round-trip a plan in a test and assert your codec did the work. Both being installed does not mean your node reached you. → {ref}extension_codec_order

Bundles and planners

  • [ ] You ship a bundle, not loose pieces, if you have codecs or a planner. → {ref}extension_bundles
  • [ ] Your bundle is configuration-only. Fresh components on every call, no cached bound components, no retaining the context passed in, no registering anything on it — a factory that mutates the context is not rolled back if a later factory raises. → {ref}extension_bundles
  • [ ] Your codecs are objects exposing the getter, not bare capsules. with_extensions refuses a capsule, because there would be nothing to name the codec by. → {ref}extension_bundles_codecs_are_objects
  • [ ] Your planner hook wraps fallback and delegates to it. Ignoring it replaces every layer beneath you, which is legal but not composable. → {ref}extension_bundles
  • [ ] Your planner hook returns None, not fallback, when it has nothing to contribute. Returning fallback installs the session's own planner as a foreign one and adds an FFI hop that was not there. → {ref}extension_bundles
  • [ ] You read the host's codec chains in the planner hook, not the extension hook. Phase one runs before anything is installed. → {ref}extension_bundles_two_phases
  • [ ] If you also offer the low-level path, document that codecs go in before a layered planner. → {ref}planner_codec_rebinding

Packaging and documentation

  • [ ] You state which datafusion version your release requires. A mismatch raises an ImportError on import, which is a good failure — but only if your users know what to install. → {ref}extension_version_mismatch
  • [ ] You tell your users to keep a context alive for as long as anything derived from it is in use. This is the rule most likely to arrive as a bug report against your library. → {ref}extension_sessions
  • [ ] Your production codec serializes durable metadata, not a process-local token. The examples in this repository use tokens to make ownership observable; that is a demonstration, not a pattern. → {ref}extension_codec_durable_metadata
  • [ ] You have integration tests across a real FFI boundary. The two example crates in this repository are the pattern: build the cdylib, install the wheel, then exercise it from Python.