Skip to content

[chore] Release v1.53.1#13676

Merged
kmcgrady merged 11 commits intodevelopfrom
release/1.53.1
Jan 22, 2026
Merged

[chore] Release v1.53.1#13676
kmcgrady merged 11 commits intodevelopfrom
release/1.53.1

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Automated PR to merge release branch into develop.

Release version: 1.53.1

kmcgrady and others added 8 commits January 13, 2026 17:46
## Describe your changes
* `st.metric` Markdown support, color palette, and format
* Sidebar width
* `st.select_slider` Markdown in `options`
* `st.logo` homepage link
* Per-widget max upload size
* Add **or** delete rows in `st.data_editor` (or both)
* Float button icons right
* Caching scope and caller's rights `SnowflakeConnection`
* CCV2 all content optional
* Expose OIDC tokens

## Screenshot or video (only for visual changes)
n/a

## GitHub Issue Link (if applicable)
n/a

## Testing Plan
None. Docstrings only.

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
## Describe your changes

Fix a regression where creating container elements (`st.columns`,
`st.tabs`) inside a container within a `st.spinner` context caused the
app to crash with "Bad delta path index" errors.

The root cause was that `SetNodeByDeltaPathVisitor.visitTransientNode`
incorrectly consumed an index from the delta path when visiting
TransientNodes, while `GetNodeByDeltaPathVisitor` correctly treats them
as transparent. This caused delta paths to become misaligned with the
actual tree structure.

The fix treats TransientNode as transparent in the delta path hierarchy
and preserves the transient wrapper when modifying nodes inside its
anchor.

## GitHub Issue Link (if applicable)

Closes #13658

## Testing Plan

- Unit Tests (JS): Updated and added tests for
`SetNodeByDeltaPathVisitor.visitTransientNode` to verify the fix
- E2E Tests: Added regression test
`test_spinner_with_container_elements` that reproduces the exact
scenario from the issue
- All 5630 frontend tests pass
- All 201 render-tree tests pass

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.

---------

Co-authored-by: Ken McGrady <[email protected]>
Co-authored-by: Claude <[email protected]>
## Describe your changes

Fixes an issue with Snowflake connections not getting re-initialized
after having been closed.

<details>
<summary>Claude issue analysis</summary> 
# Issue Report: Snowflake Connection "Connection is closed" Error

## Summary

After recent PRs adding `on_release` to `st.cache_resource` and
session-scoped connection support, users may encounter a
`snowflake.connector.errors.DatabaseError: 250002 (08003): Connection is
closed` error when using `st.connection("snowflake")` with cached data
queries.

## Related PRs

- **PR #13439**: Add `on_release` to `st.cache_resource`
- **PR #13482**: Add session scoping to caches
- **PR #13538**: Add `SnowflakeCallersRightsConnection`
- **PR #13506**: Add session-scoped connection support

## Error Details

```
Traceback (most recent call last):
  File ".../streamlit/runtime/scriptrunner/exec_code.py", line 129, in exec_func_with_error_handling
    result = func()
  ...
  File ".../snowflake/snowpark/_internal/server_connection.py", line 205, in _cursor
    self._thread_store.cursor = self._conn.cursor()
  File ".../snowflake/connector/connection.py", line 1270, in cursor
    Error.errorhandler_wrapper(...)
snowflake.connector.errors.DatabaseError: 250002 (08003): Connection is closed
```

## Root Cause Analysis

### Background

The PRs mentioned added important functionality:

1. **PR #13439** added the `on_release` callback to `st.cache_resource`,
which is called when cache entries are evicted
2. **PR #13506** modified `connection_factory.py` to use this
`on_release` callback to call `connection.close()` when a connection is
evicted from the cache

This is the relevant code in `connection_factory.py`:

```python
def on_release_wrapped(connection: ConnectionClass) -> None:
    connection.close()

__create_connection = cache_resource(
    max_entries=max_entries,
    show_spinner="Running `st.connection(...)`.",
    ttl=ttl,
    scope=scope,
    on_release=on_release_wrapped,  # Calls close() when evicted
)(__create_connection)
```

### The Bug

In `BaseSnowflakeConnection.close()`, after calling
`self._raw_instance.close()`, the `_raw_instance` attribute was **NOT**
reset to `None`:

```python
def close(self) -> None:
    """Closes the underlying Snowflake connection."""
    if self._raw_instance is not None:
        self._raw_instance.close()
        # BUG: _raw_instance was NOT set to None!
```

This caused the following issue:

1. When `close()` was called (e.g., via `on_release` when a cache entry
is evicted), the underlying connection was closed
2. However, `_raw_instance` still referenced the **closed** connection
object
3. The `_instance` property checks `if self._raw_instance is None` to
decide whether to create a new connection:

   ```python
   @Property
   def _instance(self) -> RawConnectionT:
       if self._raw_instance is None:
           self._raw_instance = self._connect(**self._kwargs)
       return self._raw_instance
   ```

4. Since `_raw_instance` wasn't `None`, subsequent access to `_instance`
returned the **CLOSED** connection
5. Any operations on the closed connection failed with "Connection is
closed"

### When This Bug Manifests

The `on_release` callback (which calls `close()`) is triggered when:

- Cache entries expire due to TTL
- Cache is full and oldest entries are evicted (`max_entries`)
- `st.cache_resource.clear()` is called
- For session-scoped caches: when a session disconnects

For global-scoped connections like `st.connection("snowflake")`, this
typically only happens if:
- `st.cache_resource.clear()` is called explicitly
- TTL is set and expires
- `max_entries` is set and exceeded

### Additional Consideration: Snowpark Sessions

When users call `conn.session()`, they get a Snowpark Session that
internally references `self._instance`. If the underlying connection is
closed:

```python
def session(self) -> Session:
    if running_in_sis():
        return get_active_session()
    return Session.builder.configs({"connection": self._instance}).create()
```

Any Snowpark Sessions created from the connection will also fail because
they hold a reference to the now-closed underlying connection object.

## Fix

The fix is simple: reset `_raw_instance` to `None` after closing the
connection:

```python
def close(self) -> None:
    """Closes the underlying Snowflake connection."""
    if self._raw_instance is not None:
        self._raw_instance.close()
        self._raw_instance = None  # Added this line
```

This ensures that after `close()` is called, the next access to
`_instance` will create a new connection instead of returning the closed
one.

## Files Changed

1. **`lib/streamlit/connections/snowflake_connection.py`**
- Fixed `close()` method to reset `_raw_instance = None` after closing

2. **`lib/tests/streamlit/connections/snowflake_connection_test.py`**
   - Added `TestSnowflakeConnectionClose` test class with:
- `test_close_resets_raw_instance`: Verifies that `close()` closes the
connection AND resets `_raw_instance`
- `test_close_is_noop_when_not_connected`: Verifies that `close()`
doesn't fail when `_raw_instance` is already `None`

## Testing

```bash
PYTHONPATH=lib pytest lib/tests/streamlit/connections/snowflake_connection_test.py::TestSnowflakeConnectionClose -v
```

Output:
```
lib/tests/streamlit/connections/snowflake_connection_test.py::TestSnowflakeConnectionClose::test_close_resets_raw_instance PASSED
lib/tests/streamlit/connections/snowflake_connection_test.py::TestSnowflakeConnectionClose::test_close_is_noop_when_not_connected PASSED
```

## Recommendations for Users

Until this fix is released, users experiencing this issue can:

1. **Avoid storing Snowpark Sessions long-term**: Instead of caching
Snowpark Sessions, create them fresh when needed
2. **Check if using `st.cache_resource.clear()`**: If calling this
anywhere in the app, it will close all cached connections
3. **Consider connection TTL settings**: If TTL is set on the
connection, it may expire and close

## Impact

- **Affected**: Users of `st.connection("snowflake")` and
`st.connection("snowflake-callers-rights")` who experience cache
eviction scenarios
- **Severity**: Medium - The bug causes operations to fail with a
confusing error message, but the workaround (restarting the app or
avoiding cache clears) is available
- **Scope**: Only affects `SnowflakeConnection` and its subclasses;
other connection types (`SQLConnection`, `SnowparkConnection`) inherit
the no-op `close()` from `BaseConnection` and are not affected
</details>

## GitHub Issue Link (if applicable)

## Testing Plan

- Added unit test.

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.
…13674)

## Summary
- Fixes an issue where a `TransientNode` without an anchor would not
properly capture the existing `BlockNode` when replacing it at an empty
delta path
- This preserves the original block structure so it can be restored when
transient elements (like spinners) are cleared
- Mirrors the existing behavior in `visitElementNode`

## Test plan
- [x] Added unit tests for the new behavior in
`SetNodeByDeltaPathVisitor.test.ts`
- [x] Added e2e test `test_spinner_with_delayed_container_write` that
verifies container content written after a spinner renders appears
correctly
- [x] All existing unit tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <[email protected]>
…13648)

## Describe your changes

Fixes issue #13646 where `st.multiselect()` and `st.selectbox` clears
user selections after each script rerun when using custom class objects
without `__eq__` implementation and a `format_func` parameter. This was
a regression introduced in PR #13448.

**Root Cause**: The validation logic compared multiselect values using
`==`, which falls back to identity comparison for objects without
`__eq__`. Since `register_widget()` deepcopies widget values, the new
instances fail identity comparison with original options, causing valid
selections to be filtered out.

**Solution**: Changed validation to compare values using `format_func()`
by their formatted string representation instead of using `==`. This is
more robust and works correctly regardless of whether custom classes
implement `__eq__`.

## Testing Plan

- Unit Tests: Added 4 comprehensive test cases covering custom objects
without `__eq__`, partial matches, objects with `__str__`, and edge
cases where `format_func` fails on incompatible types
- E2E Tests: Added `test_multiselect_custom_objects_without_eq` to
verify selections persist across script reruns with custom class objects
- Backward Compatibility: The `format_func` parameter has a default
value of `str`, so existing code continues to work without changes

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.

---------

Co-authored-by: Claude <[email protected]>
…umn` (#13663)

## Describe your changes

Allows selecting text of pills in the MultiselectColumn and ListColumn.
To do this, I have moved a fixed version of the multi-select cell
implementation from glide-data-grid to Streamlit since the release
process is currently broken on glide-data-grid. This will be a bit more
of a temporary solution, and it's planned to switch back soon.

- Closes #13646

## Testing Plan

- Added unit tests. 

---

**Contribution License Agreement**

By submitting this pull request you agree that all contributions to this
project are made under the Apache 2.0 license.

---------

Co-authored-by: Claude <[email protected]>
@github-actions github-actions bot requested a review from a team as a code owner January 22, 2026 19:00
@github-actions github-actions bot added change:chore PR contains maintenance or housekeeping change impact:users PR changes affect end users labels Jan 22, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Jan 22, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@github-actions
Copy link
Copy Markdown
Contributor Author

github-actions bot commented Jan 22, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13676/streamlit-1.53.1-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13676.streamlit.app (☁️ Deploy here if not accessible)

@kmcgrady kmcgrady merged commit 6adcc22 into develop Jan 22, 2026
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:chore PR contains maintenance or housekeeping change impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants