)]}'
{
  "commit": "7df58e531bb949785cae3cca488a1ae55cb6d478",
  "tree": "76528a4da5662f5fbd027a176bdbc24652f60f54",
  "parents": [
    "987228300b1c52215b4bb10a1cd5781c40648fbe"
  ],
  "author": {
    "name": "Tim Saucer",
    "email": "timsaucer@gmail.com",
    "time": "Fri May 29 08:09:42 2026 -0400"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Fri May 29 08:09:42 2026 -0400"
  },
  "message": "feat: pass calling SessionContext to Python UDTF callbacks (#1555)\n\n* feat: pass calling SessionContext to Python UDTF callbacks\n\nDataFusion 53 added `TableFunctionImpl::call_with_args(TableFunctionArgs)`\nwhere `TableFunctionArgs` carries both the positional expression\narguments and the calling `\u0026dyn Session`. The pure-Python UDTF path\npreviously discarded everything but the exprs.\n\nThread the session through when the user callback\u0027s signature opts in\nby declaring a `session` keyword parameter (or `**kwargs`). At call\ntime we downcast the `\u0026dyn Session` to its canonical `SessionState`\nimpl and build a fresh `SessionContext` over the same Arc-shared state,\nexposed to Python as a `datafusion.SessionContext` wrapper. Existing\ncallbacks whose signatures do not declare `session` continue to be\ncalled with the positional expression arguments only — no behavior\nchange for current users.\n\nNote: a UDTF body cannot drive a fresh `ctx.sql(...).collect()` on the\npassed-in session because the outer SQL execution already holds the\ntokio runtime. Use the session for metadata access (catalogs, UDF\nlookups, config) rather than nested DataFrame collection.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* docs: clarify py_session_from_session downcast is defensive\n\nThe doc comment implied a foreign FFI session was a real input. No\ncurrent path reaches a pure-Python UDTF with a non-SessionState\nsession: the SQL planner and __call__ both hand a SessionState, and a\nForeignSession would only arrive via FFI-export of the UDTF, which\ndatafusion-python does not do. Reword to state the guard is defensive\nand rewrap the error string.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* refactor: opt-in UDTF session injection via with_session flag\n\nReplaces signature sniffing with an explicit ``with_session\u003dTrue`` kwarg\non ``TableFunction`` / ``udtf``. Avoids name-based detection footguns\n(positional-only ``session`` params, accidental ``**kwargs`` opt-in,\nshadowing by unrelated params) and makes author intent visible at\nregistration. Also documents the feature in the UDTF user guide.\n\nRust field renamed ``accepts_session`` -\u003e ``inject_session_on_call`` to\nmatch the Python-side opt-in semantics.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n\n* fix: reject with_session\u003dTrue for FFI UDTFs and qualify mutation docs\n\nRaise TypeError when with_session\u003dTrue is combined with an FFI-exported\ntable function (one exposing __datafusion_table_function__). The Rust\nFFI branch does not consult the flag, so it would silently be dropped;\nguard both TableFunction.__init__ and the udtf() convenience entry.\n\nQualify the doc claim that mutations through the injected session\npropagate to the caller: registry mutations do (shared Arc registries),\nbut config changes do not (SessionConfig is cloned). Mirror the caveat\nin TableFunction.__init__ per the user-guide caveats convention.\n\nCo-Authored-By: Claude Opus 4.7 \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "b3de25e523c2ca64f0bb3a1655cb46bc31a4094d",
      "old_mode": 33188,
      "old_path": "crates/core/src/udtf.rs",
      "new_id": "cffa0c12a7d29c34ded39a57b474c20130fcdd3d",
      "new_mode": 33188,
      "new_path": "crates/core/src/udtf.rs"
    },
    {
      "type": "modify",
      "old_id": "59c47b5957a7b4c602473e42885e442613134a61",
      "old_mode": 33188,
      "old_path": "docs/source/user-guide/common-operations/udf-and-udfa.rst",
      "new_id": "918c2e29e3f6b5175932bb46bc318f17a31e0bd2",
      "new_mode": 33188,
      "new_path": "docs/source/user-guide/common-operations/udf-and-udfa.rst"
    },
    {
      "type": "modify",
      "old_id": "ced707a9695c850ba80836f7cbb3dff85fa4d89a",
      "old_mode": 33188,
      "old_path": "python/datafusion/user_defined.py",
      "new_id": "81a516af8b928bf05163be8135d44885570147d2",
      "new_mode": 33188,
      "new_path": "python/datafusion/user_defined.py"
    },
    {
      "type": "modify",
      "old_id": "925a8ba01e00a806ce0fe521c0f08ddf8ffa8937",
      "old_mode": 33188,
      "old_path": "python/tests/test_udtf.py",
      "new_id": "dcb2bacc379b42b93f2c59efc61e0d42f89f08fd",
      "new_mode": 33188,
      "new_path": "python/tests/test_udtf.py"
    }
  ]
}
