Skip to content

Add Iceberg snapshot-id time travel support (version=)#4231

Open
sfc-gh-igarish wants to merge 4 commits into
mainfrom
igarish/snowpark-python-iceberg-snapshot-id
Open

Add Iceberg snapshot-id time travel support (version=)#4231
sfc-gh-igarish wants to merge 4 commits into
mainfrom
igarish/snowpark-python-iceberg-snapshot-id

Conversation

@sfc-gh-igarish
Copy link
Copy Markdown

Adds a new version field to TimeTravelConfig that emits the Snowflake AT(VERSION => <snapshot_id>) SQL clause for Iceberg snapshot id time travel. The new kwarg is wired into:

  • snowflake.snowpark.Session.table(version=...)
  • snowflake.snowpark.DataFrameReader.table(version=...)
  • snowflake.snowpark.Table(version=...)

For Spark Iceberg compatibility, DataFrameReader.option("snapshot-id", N) (and the snapshot_id variant) is also accepted: the helper _extract_time_travel_from_options aliases it to the version kwarg and auto-sets time_travel_mode="at". String snapshot ids are coerced to int.

Validation:

  • version requires time_travel_mode='at' (matches Snowflake grammar and Spark Iceberg semantics of "snapshot N", not "before N")
  • version must be int (bool explicitly rejected)
  • exactly one of statement/offset/timestamp/stream/version

Mirrors the existing iceberg_tag pattern. Tests in tests/unit/test_utils.py cover SQL emission, validation errors, and the Spark-compat option aliases.

  1. Which Jira issue is this PR addressing? Make sure that there is an accompanying issue to your PR.

    Fixes SNOW-NNNNNNN

  2. Fill out the following pre-review checklist:

    • I am adding a new automated test(s) to verify correctness of my new code
      • If this test skips Local Testing mode, I'm requesting review from @snowflakedb/local-testing
    • I am adding new logging messages
    • I am adding a new telemetry message
    • I am adding new credentials
    • I am adding a new dependency
    • If this is a new feature/behavior, I'm adding the Local Testing parity changes.
    • I acknowledge that I have ensured my changes to be thread-safe. Follow the link for more information: Thread-safe Developer Guidelines
    • If adding any arguments to public Snowpark APIs or creating new public Snowpark APIs, I acknowledge that I have ensured my changes include AST support. Follow the link for more information: AST Support Guidelines
  3. Please describe how your code solves the related issue.

    Please write a short description of how your code change solves the related issue.

@sfc-gh-igarish sfc-gh-igarish requested review from a team as code owners May 21, 2026 18:25
@sfc-gh-igarish sfc-gh-igarish marked this pull request as draft May 21, 2026 18:25
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 21, 2026

Codecov Report

❌ Patch coverage is 80.64516% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.39%. Comparing base (9cb0c3e) to head (f519b75).

Files with missing lines Patch % Lines
src/snowflake/snowpark/dataframe_reader.py 88.88% 1 Missing and 1 partial ⚠️
src/snowflake/snowpark/session.py 33.33% 1 Missing and 1 partial ⚠️
src/snowflake/snowpark/table.py 33.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4231      +/-   ##
==========================================
- Coverage   95.40%   95.39%   -0.02%     
==========================================
  Files         171      171              
  Lines       44205    44235      +30     
  Branches     7548     7557       +9     
==========================================
+ Hits        42174    42198      +24     
- Misses       1247     1250       +3     
- Partials      784      787       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sfc-gh-igarish sfc-gh-igarish marked this pull request as ready for review May 21, 2026 20:23
Copy link
Copy Markdown
Collaborator

@sfc-gh-mayliu sfc-gh-mayliu left a comment

Choose a reason for hiding this comment

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

Is there a Snowflake doc explaining that the AT(VERSION=> <id>) syntax is officially supported? Is VERSION/snapshot-id specific to iceberg tables only? It'd be great to get more context for the review.

Also, could you add some integ tests in test_dataframe.py for E2E validation?

@sfc-gh-igarish
Copy link
Copy Markdown
Author

sfc-gh-igarish commented May 21, 2026

Is there a Snowflake doc explaining that the AT(VERSION=> <id>) syntax is officially supported? Is VERSION/snapshot-id specific to iceberg tables only? It'd be great to get more context for the review.

Also, could you add some integ tests in test_dataframe.py for E2E validation?

Yes this is for unmanaged Iceberg table only.

https://docs.google.com/document/d/1Z7GlEWbqZRCYr4NDjh-7klgvJJAp0jF45p9GH1MbQ4M/edit?tab=t.0

Copy link
Copy Markdown
Collaborator

@sfc-gh-mayliu sfc-gh-mayliu left a comment

Choose a reason for hiding this comment

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

As per offline discussions, please look into:

  1. add parameter protection for this change to isolate downstream SCOS use and Snowpark-python usage
  2. add integ tests to validate E2E behaviors; if Iceberg credentials cannot be resolved systematically, mark it with pytest.skip but reusable for manual testing

sfc-gh-igarish added a commit that referenced this pull request May 22, 2026
…enabled

Adds an umbrella session flag (default ``False``) that gates the
``version=`` / ``snapshot-id`` time-travel surface added by the previous
commit:

  * ``Session.iceberg_features_enabled`` — public bool property + setter
    (locking + bool coercion). Mirrors the existing pattern used by
    ``sql_simplifier_enabled`` / ``cte_optimization_enabled``.
  * ``Session._require_iceberg_features_enabled(feature=...)`` — internal
    helper that raises ``SnowparkClientException`` pointing users at the
    flag.

Gates are placed at all three entry points so the kwarg and the Spark
Iceberg ``snapshot-id`` / ``snapshot_id`` reader option are both
rejected when the flag is off:

  * ``Session.table(name, version=...)``
  * ``DataFrameReader.table(name, version=...)`` and
    ``.option("snapshot-id", N).table(...)`` (also catches ``snapshot_id``)
  * ``Table(name, ..., version=...)`` direct construction

Default ``False`` because the underlying Snowflake feature
(``AT(VERSION => N)`` on unmanaged Iceberg tables) is itself gated
behind the ``FEATURE_ICEBERG_TIME_TRAVEL`` account parameter and
currently scoped to Catalog-Linked Databases. Snowpark Connect (SAS)
flips the flag per-session when its
``snowpark.connect.iceberg.timeTravel.enabled`` config is set.

Tests
-----

``tests/unit/test_iceberg_features_flag.py`` (new, 13 tests):

  * Default + setter + helper semantics.
  * Each of the three entry-point gates raises when the flag is off and
    is bypassed cleanly when the flag is on.
  * Three AST-emission coverage tests patch ``with_src_position`` to
    return a stand-in with a ``version`` field, so the
    ``hasattr(ast, "version")``-guarded ``ast.version.value = version``
    line in ``session.py`` / ``dataframe_reader.py`` / ``table.py``
    actually executes. This addresses the codecov gap (3 missing lines
    flagged by the patch report on PR #4231).

``tests/integ/test_dataframe.py`` (3 new tests, all
``@pytest.mark.skip``):

  * End-to-end ``Session.table(..., version=N)`` against a multi-snapshot
    Iceberg table.
  * End-to-end ``session.read.option('snapshot-id', N).table(...)``.
  * End-to-end gate verification (flag off + ``version=`` → raises).

  All three are skipped because they need a Catalog-Linked Database
  (cldUnity / cldglue) with an unmanaged Iceberg table that has multiple
  snapshots AND ``FEATURE_ICEBERG_TIME_TRAVEL`` enabled on the account
  — neither of which the standard snowpark-python integ accounts have
  today. They run manually against sfctest0. A TODO above the block
  tracks wiring them into CI once an Iceberg-capable integ account
  exists.

Addresses @sfc-gh-mayliu review comments on PR #4231 (integ test
coverage) and the codecov patch-coverage drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sfc-gh-igarish
Copy link
Copy Markdown
Author

As per offline discussions, please look into:

  1. add parameter protection for this change to isolate downstream SCOS use and Snowpark-python usage
  2. add integ tests to validate E2E behaviors; if Iceberg credentials cannot be resolved systematically, mark it with pytest.skip but reusable for manual testing

@sfc-gh-mayliu thanks for the review. Pushed commit 696b37e5d addressing both review points and the codecov patch-coverage drop.

1. Snowflake doc / scope of AT(VERSION => N)

Yes — this syntax is the supported way to do snapshot-id time travel on unmanaged Iceberg tables in Catalog-Linked Databases (Unity / Glue). Spec lives in this design doc and is gated server-side behind the FEATURE_ICEBERG_TIME_TRAVEL account parameter. Does not apply to Snowflake-managed Iceberg tables or to non-Iceberg tables.

2. Gating the Snowpark surface

Because the underlying Snowflake feature is itself account-gated, this PR now also gates the Snowpark API behind a session flag, default False:

```python
session.iceberg_features_enabled = True # opt-in
session.table("t", time_travel_mode="at", version=N)
session.read.option("snapshot-id", N).table("t")
```

With the flag off, all three entry points (Session.table, DataFrameReader.table with either version= or snapshot-id/snapshot_id options, and direct Table(...) construction) raise SnowparkClientException pointing to the flag. SCOS / snowpark-connect flips the flag per-session via its snowpark.connect.iceberg.timeTravel.enabled config.

3. Integ tests in test_dataframe.py

Added three E2E tests (@pytest.mark.skip for now):

  • test_iceberg_snapshot_id_time_travel_session_tableSession.table(..., version=N)
  • test_iceberg_snapshot_id_time_travel_dataframe_reader_optionsession.read.option('snapshot-id', N).table(...) matches the explicit kwarg path
  • test_iceberg_snapshot_id_flag_gates_version_kwarg — flag-off raises before any SQL is issued

Skipped because they need a CLD (cldUnity / cldglue) with a multi-snapshot unmanaged Iceberg table AND FEATURE_ICEBERG_TIME_TRAVEL enabled on the account — neither of which the standard integ accounts have today. Run manually against sfctest0. A TODO above the block tracks unskipping once we wire up an Iceberg-capable integ account.

4. Codecov patch coverage

Added tests/unit/test_iceberg_features_flag.py (13 tests). Three of those patch with_src_position to return a stand-in with a version field, exercising the hasattr(ast, "version")-guarded ast.version.value = version lines in session.py, dataframe_reader.py, and table.py — the exact lines codecov flagged. Confirmed locally via coverage json that all three are now covered.

Copy link
Copy Markdown
Collaborator

@sfc-gh-mayliu sfc-gh-mayliu left a comment

Choose a reason for hiding this comment

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

Thanks for protecting the code with flag, it makes the change much safer. Have a few more thoughts below. Also, could you please attach a manual test result for the skipped integ test that requires CLD?

Comment thread src/snowflake/snowpark/dataframe_reader.py Outdated
Comment thread src/snowflake/snowpark/dataframe_reader.py Outdated
Comment thread tests/integ/test_dataframe.py Outdated
Comment thread src/snowflake/snowpark/table.py Outdated
Comment thread tests/integ/test_dataframe.py Outdated
Comment thread CHANGELOG.md Outdated
sfc-gh-igarish added a commit that referenced this pull request May 26, 2026
…enabled

Adds an umbrella session flag (default ``False``) that gates the
``version=`` / ``snapshot-id`` time-travel surface added by the previous
commit:

  * ``Session.iceberg_features_enabled`` — public bool property + setter
    (locking + bool coercion). Mirrors the existing pattern used by
    ``sql_simplifier_enabled`` / ``cte_optimization_enabled``.
  * ``Session._require_iceberg_features_enabled(feature=...)`` — internal
    helper that raises ``SnowparkClientException`` pointing users at the
    flag.

Gates are placed at all three entry points so the kwarg and the Spark
Iceberg ``snapshot-id`` / ``snapshot_id`` reader option are both
rejected when the flag is off:

  * ``Session.table(name, version=...)``
  * ``DataFrameReader.table(name, version=...)`` and
    ``.option("snapshot-id", N).table(...)`` (also catches ``snapshot_id``)
  * ``Table(name, ..., version=...)`` direct construction

Default ``False`` because the underlying Snowflake feature
(``AT(VERSION => N)`` on unmanaged Iceberg tables) is itself gated
behind the ``FEATURE_ICEBERG_TIME_TRAVEL`` account parameter and
currently scoped to Catalog-Linked Databases. Snowpark Connect (SAS)
flips the flag per-session when its
``snowpark.connect.iceberg.timeTravel.enabled`` config is set.

Tests
-----

``tests/unit/test_iceberg_features_flag.py`` (new, 13 tests):

  * Default + setter + helper semantics.
  * Each of the three entry-point gates raises when the flag is off and
    is bypassed cleanly when the flag is on.
  * Three AST-emission coverage tests patch ``with_src_position`` to
    return a stand-in with a ``version`` field, so the
    ``hasattr(ast, "version")``-guarded ``ast.version.value = version``
    line in ``session.py`` / ``dataframe_reader.py`` / ``table.py``
    actually executes. This addresses the codecov gap (3 missing lines
    flagged by the patch report on PR #4231).

``tests/integ/test_dataframe.py`` (3 new tests, all
``@pytest.mark.skip``):

  * End-to-end ``Session.table(..., version=N)`` against a multi-snapshot
    Iceberg table.
  * End-to-end ``session.read.option('snapshot-id', N).table(...)``.
  * End-to-end gate verification (flag off + ``version=`` → raises).

  All three are skipped because they need a Catalog-Linked Database
  (cldUnity / cldglue) with an unmanaged Iceberg table that has multiple
  snapshots AND ``FEATURE_ICEBERG_TIME_TRAVEL`` enabled on the account
  — neither of which the standard snowpark-python integ accounts have
  today. They run manually against sfctest0. A TODO above the block
  tracks wiring them into CI once an Iceberg-capable integ account
  exists.

Addresses @sfc-gh-mayliu review comments on PR #4231 (integ test
coverage) and the codecov patch-coverage drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sfc-gh-igarish sfc-gh-igarish force-pushed the igarish/snowpark-python-iceberg-snapshot-id branch from 1c72e04 to ce01976 Compare May 26, 2026 18:54
Comment thread tests/unit/test_iceberg_features_flag.py Outdated
Comment thread src/snowflake/snowpark/dataframe_reader.py
Comment thread src/snowflake/snowpark/session.py
Comment thread src/snowflake/snowpark/dataframe_reader.py
Comment thread src/snowflake/snowpark/dataframe_reader.py Outdated
@sfc-gh-igarish
Copy link
Copy Markdown
Author

Pushed 7563e82 to address review feedback from @sfc-gh-aling and @sfc-gh-mayliu.

1. Drop iceberg_features_enabled umbrella flag (Aling)
The session property, setter, and _require_iceberg_features_enabled helper are removed, along with all per-entry-point gate calls in Session.table / DataFrameReader.table / Table.__init__. Net effect: no opt-in flag, no mental burden for Snowpark users, and no SnowparkClientException when they happen to read a CLD Iceberg table. The underlying FEATURE_ICEBERG_TIME_TRAVEL account parameter already provides server-side isolation, so a client-side gate was redundant.

2. Move version= out of public signatures via **kwargs (Aling)
All three entry points now use version = kwargs.pop("version", None) instead of an explicit kwarg. version is gone from public signatures, docstrings, doctest examples, and Sphinx-rendered API. The option("version", ...) alias is dropped too. The Spark Iceberg option("snapshot-id", ...) / option("snapshot_id", ...) paths remain (external Spark-compat surface, auto-set time_travel_mode='at' exactly as before).

3. Duplicate copyright header in test_iceberg_features_flag.py (May)
File removed entirely (only tested the now-removed flag).

4. Tests

  • tests/unit/test_iceberg_features_flag.py deleted.
  • tests/unit/test_utils.py::test_extract_time_travel_snapshot_id_option trimmed to cover only snapshot-id / snapshot_id cases.
  • tests/integ/test_dataframe.py::test_iceberg_snapshot_id_flag_gates_version_kwarg removed (no flag to gate). The two skipped CLD-required E2E tests stay, minus the iceberg_features_enabled = True setup lines.

Local: tests/unit/test_utils.py (15), tests/unit/test_dataframe.py + tests/unit/test_session.py + tests/unit/test_dataframe_reader_type_parsing.py (237+1 skipped) all green.

Diff is now +36 / −313 (mostly removal). PTAL when you have a moment, thanks!

Copy link
Copy Markdown
Collaborator

@sfc-gh-mayliu sfc-gh-mayliu left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for supporting the new time travel options for iceberg

@sfc-gh-igarish
Copy link
Copy Markdown
Author

CI note — 6 failing tests are unrelated to this PR

The three failing Test py-ubuntu-latest-64-cores-3.{9,13,14}-aws jobs in run 26541665563 all hit the same 6 failures in tests/integ/test_dataframe_ai.py:

  • test_dataframe_ai_split_text_markdown_header_basic
  • test_dataframe_ai_split_text_markdown_header_default_output
  • test_dataframe_ai_split_text_recursive_character_basic
  • test_dataframe_ai_split_text_recursive_character_markdown_format
  • test_dataframe_ai_split_text_recursive_character_custom_separators
  • test_dataframe_ai_split_text_recursive_character_default_output

All six raise the same SQL compilation error:

snowflake.snowpark.exceptions.SnowparkSQLException: (1304): ... 002001 (02000):
SQL compilation error:
Object 'snowflake.snowpark.anaconda_shared_repository' does not exist or not authorized.

This is an environmental issue with the CI Snowflake account (missing / unauthorized anaconda_shared_repository object that DataFrameAIFunctions.split_text_* looks up), not a code regression from this PR:

  • This PR's diff (+36 / −313) touches only the Iceberg snapshot-id time travel path (session.py, dataframe_reader.py, table.py, _internal/utils.py) and their tests. None of dataframe_ai_functions.py / test_dataframe_ai.py / any Cortex code is modified.
  • The previous commit on this branch (ce019767d, before today's push) passed the same py-ubuntu-latest-64-cores-3.14-aws job cleanly.
  • main already has precedent for these AI tests being flaky — dd6d6b3bb ("SNOW-3201438: temporarily skipping failing ai function tests") landed an explicit skip-list for the same family of tests; the split_text_* cases appear to be a second wave that slipped past that earlier skip.

I've triggered a re-run of the three failed jobs in case the underlying object access was transient. If they fail the same way again, this is firmly a dataframe_ai ownership / CI-account fix, not something to gate this PR on.

sfc-gh-igarish and others added 2 commits May 27, 2026 18:43
Adds a new ``version`` field to ``TimeTravelConfig`` that emits the
Snowflake ``AT(VERSION => <snapshot_id>)`` SQL clause for Iceberg snapshot
id time travel. The new kwarg is wired into:

  - ``snowflake.snowpark.Session.table(version=...)``
  - ``snowflake.snowpark.DataFrameReader.table(version=...)``
  - ``snowflake.snowpark.Table(version=...)``

For Spark Iceberg compatibility, ``DataFrameReader.option("snapshot-id", N)``
(and the ``snapshot_id`` variant) is also accepted: the helper
``_extract_time_travel_from_options`` aliases it to the ``version`` kwarg
and auto-sets ``time_travel_mode="at"``. String snapshot ids are coerced
to int.

Validation:
  - ``version`` requires ``time_travel_mode='at'`` (matches Snowflake
    grammar and Spark Iceberg semantics of "snapshot N", not "before N")
  - ``version`` must be ``int`` (bool explicitly rejected)
  - exactly one of statement/offset/timestamp/stream/version

Mirrors the existing ``iceberg_tag`` pattern. Tests in
``tests/unit/test_utils.py`` cover SQL emission, validation errors, and
the Spark-compat option aliases.

Co-authored-by: Cursor <cursoragent@cursor.com>
…enabled

Adds an umbrella session flag (default ``False``) that gates the
``version=`` / ``snapshot-id`` time-travel surface added by the previous
commit:

  * ``Session.iceberg_features_enabled`` — public bool property + setter
    (locking + bool coercion). Mirrors the existing pattern used by
    ``sql_simplifier_enabled`` / ``cte_optimization_enabled``.
  * ``Session._require_iceberg_features_enabled(feature=...)`` — internal
    helper that raises ``SnowparkClientException`` pointing users at the
    flag.

Gates are placed at all three entry points so the kwarg and the Spark
Iceberg ``snapshot-id`` / ``snapshot_id`` reader option are both
rejected when the flag is off:

  * ``Session.table(name, version=...)``
  * ``DataFrameReader.table(name, version=...)`` and
    ``.option("snapshot-id", N).table(...)`` (also catches ``snapshot_id``)
  * ``Table(name, ..., version=...)`` direct construction

Default ``False`` because the underlying Snowflake feature
(``AT(VERSION => N)`` on unmanaged Iceberg tables) is itself gated
behind the ``FEATURE_ICEBERG_TIME_TRAVEL`` account parameter and
currently scoped to Catalog-Linked Databases. Snowpark Connect (SAS)
flips the flag per-session when its
``snowpark.connect.iceberg.timeTravel.enabled`` config is set.

Tests
-----

``tests/unit/test_iceberg_features_flag.py`` (new, 13 tests):

  * Default + setter + helper semantics.
  * Each of the three entry-point gates raises when the flag is off and
    is bypassed cleanly when the flag is on.
  * Three AST-emission coverage tests patch ``with_src_position`` to
    return a stand-in with a ``version`` field, so the
    ``hasattr(ast, "version")``-guarded ``ast.version.value = version``
    line in ``session.py`` / ``dataframe_reader.py`` / ``table.py``
    actually executes. This addresses the codecov gap (3 missing lines
    flagged by the patch report on PR #4231).

``tests/integ/test_dataframe.py`` (3 new tests, all
``@pytest.mark.skip``):

  * End-to-end ``Session.table(..., version=N)`` against a multi-snapshot
    Iceberg table.
  * End-to-end ``session.read.option('snapshot-id', N).table(...)``.
  * End-to-end gate verification (flag off + ``version=`` → raises).

  All three are skipped because they need a Catalog-Linked Database
  (cldUnity / cldglue) with an unmanaged Iceberg table that has multiple
  snapshots AND ``FEATURE_ICEBERG_TIME_TRAVEL`` enabled on the account
  — neither of which the standard snowpark-python integ accounts have
  today. They run manually against sfctest0. A TODO above the block
  tracks wiring them into CI once an Iceberg-capable integ account
  exists.

Addresses @sfc-gh-mayliu review comments on PR #4231 (integ test
coverage) and the codecov patch-coverage drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
sfc-gh-igarish and others added 2 commits May 27, 2026 18:43
- Make `option("version", ...)` auto-set `time_travel_mode="at"` so all
  three aliases (snapshot-id / snapshot_id / version) share the same
  semantics and the docstring claim "Automatically sets
  time_travel_mode='at'" holds for every documented path. Reject
  `option("version", N) + time_travel_mode="before"` for the same reason
  Snowflake rejects it for snapshot-id: AT(VERSION => N) is the only
  valid form.
- Extend the `iceberg_features_enabled` gate in `DataFrameReader.table`
  to also cover `option("version", ...)`. Previously a user could
  bypass the umbrella flag through the Snowpark-native reader option.
- Drop the dead `version` AST emission lines (and matching unit tests)
  in `Session.table`, `DataFrameReader.table`, and `Table.__init__`.
  The Table / ReadTable protos have no `version` field and the feature
  is parameter-protected (gated behind `iceberg_features_enabled`,
  consumed only by Snowpark Connect), so AST replay/telemetry is not
  warranted today. Comments at each call site point to the one-line
  restore when the proto is extended.
- Drop the Iceberg snapshot-id entry from `CHANGELOG.md` since the
  feature is gated off by default and only Snowpark Connect flips it.
- Unskip `test_iceberg_snapshot_id_flag_gates_version_kwarg` — it only
  exercises the client-side raise and needs neither a CLD nor an
  Iceberg table. Also broaden it to cover the reader paths
  (`session.read.table(version=...)`, `option("snapshot-id", ...)`,
  `option("version", ...)`).
- Swap the `TODO(SNOW-NNNNNNN)` placeholder above the skipped CLD
  integ tests for the actual parent JIRA, SNOW-3525585.

Co-authored-by: Cursor <cursoragent@cursor.com>
Addresses review feedback from May Lieu and Aling:

- Aling: drop the umbrella ``Session.iceberg_features_enabled`` property
  and ``_require_iceberg_features_enabled`` helper. The flag added more
  mental burden to direct Snowpark users without giving us a real
  isolation benefit — the underlying ``AT(VERSION => N)`` syntax is
  already account-gated by ``FEATURE_ICEBERG_TIME_TRAVEL`` server-side,
  so any caller without it on will get a server error anyway.

- May Lieu: drop ``tests/unit/test_iceberg_features_flag.py`` entirely
  (the only test for the removed flag), which also resolves the
  duplicate copyright header she flagged in that file.

To keep the ``version=`` time-travel kwarg an internal-only surface
consumed by Snowpark Connect (rather than a first-class public API),
``version`` is no longer in the public signatures of
``Session.table`` / ``DataFrameReader.table`` / ``Table.__init__``.
Each method now accepts ``**kwargs`` and pops ``version`` inside, so
direct callers can still pass it but it's not advertised in the
docstring, doctest examples, or Sphinx-rendered signature.

For the same reason, the public ``option("version", N)`` alias is
dropped (``VERSION`` removed from ``_TIME_TRAVEL_OPTIONS_PARAMS_MAP``
and from ``_extract_time_travel_from_options``). The Spark Iceberg
``option("snapshot-id", N)`` / ``option("snapshot_id", N)`` aliases
remain — those are Spark-compat surface, not Snowpark-native, and they
auto-set ``time_travel_mode='at'`` exactly as before.

Tests
-----

- ``tests/unit/test_utils.py::test_extract_time_travel_snapshot_id_option``
  is trimmed to cover only ``snapshot-id`` / ``snapshot_id``
  (the ``option("version", ...)`` cases that no longer exist are
  removed).
- ``tests/unit/test_utils.py::test_time_travel_version_snapshot_id``
  is kept — ``version`` is still a valid ``TimeTravelConfig`` field,
  just internal-only.
- Integ tests in ``tests/integ/test_dataframe.py``: the
  ``test_iceberg_snapshot_id_flag_gates_version_kwarg`` test is removed
  (no flag to gate), and ``session.iceberg_features_enabled = True``
  setup lines are dropped from the two remaining (skipped) CLD-required
  E2E tests.

Local: ``tests/unit/test_utils.py`` (15 tests) and
``tests/unit/test_dataframe.py`` + ``tests/unit/test_session.py``
(88+1 skipped) all green.

Co-authored-by: Cursor <cursoragent@cursor.com>
@sfc-gh-igarish sfc-gh-igarish force-pushed the igarish/snowpark-python-iceberg-snapshot-id branch from 7563e82 to f519b75 Compare May 28, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants