Move more stuff into connection util
diff --git a/lib/streamlit/connections/snowpark_connection.py b/lib/streamlit/connections/snowpark_connection.py
index 1fb8010e90c3..3e90a74a6ed1 100644
--- a/lib/streamlit/connections/snowpark_connection.py
+++ b/lib/streamlit/connections/snowpark_connection.py
@@ -18,17 +18,20 @@
 # way to configure this at a per-line level :(
 # mypy: no-warn-unused-ignores
 
-import configparser
-import os
 import threading
 from collections import ChainMap
 from contextlib import contextmanager
 from datetime import timedelta
-from typing import TYPE_CHECKING, Any, Dict, Iterator, Optional, Union, cast
+from typing import TYPE_CHECKING, Iterator, Optional, Union, cast
 
 import pandas as pd
 
 from streamlit.connections import ExperimentalBaseConnection
+from streamlit.connections.util import (
+    SNOWSQL_CONNECTION_FILE,
+    load_from_snowsql_config_file,
+    running_in_sis,
+)
 from streamlit.errors import StreamlitAPIException
 from streamlit.runtime.caching import cache_data
 
@@ -37,43 +40,6 @@
 
 
 _REQUIRED_CONNECTION_PARAMS = {"account"}
-_DEFAULT_CONNECTION_FILE = "~/.snowsql/config"
-
-
-def _load_from_snowsql_config_file(connection_name: str) -> Dict[str, Any]:
-    """Loads the dictionary from snowsql config file."""
-    snowsql_config_file = os.path.expanduser(_DEFAULT_CONNECTION_FILE)
-    if not os.path.exists(snowsql_config_file):
-        return {}
-
-    config = configparser.ConfigParser(inline_comment_prefixes="#")
-    config.read(snowsql_config_file)
-
-    if f"connections.{connection_name}" in config:
-        raw_conn_params = config[f"connections.{connection_name}"]
-    elif "connections" in config:
-        raw_conn_params = config["connections"]
-    else:
-        return {}
-
-    conn_params = {
-        k.replace("name", ""): v.strip('"') for k, v in raw_conn_params.items()
-    }
-
-    if "db" in conn_params:
-        conn_params["database"] = conn_params["db"]
-        del conn_params["db"]
-
-    return conn_params
-
-
-def _running_in_sis() -> bool:
-    import snowflake.connector.connection  # type: ignore
-
-    # snowflake.connector.connection.SnowflakeConnection does not exist inside a Stored
-    # Proc or Streamlit. It is only part of the external package. So this returns true
-    # only in SiS.
-    return not hasattr(snowflake.connector.connection, "SnowflakeConnection")
 
 
 class SnowparkConnection(ExperimentalBaseConnection["Session"]):
@@ -104,19 +70,19 @@ def _connect(self, **kwargs) -> "Session":
 
         # If we're running in SiS, just call get_active_session(). Otherwise, attempt to
         # create a new session from whatever credentials we have available.
-        if _running_in_sis():
+        if running_in_sis():
             return get_active_session()
 
         conn_params = ChainMap(
             kwargs,
             self._secrets.to_dict(),
-            _load_from_snowsql_config_file(self._connection_name),
+            load_from_snowsql_config_file(self._connection_name),
         )
 
         if not len(conn_params):
             raise StreamlitAPIException(
                 "Missing Snowpark connection configuration. "
-                f"Did you forget to set this in `secrets.toml`, `{_DEFAULT_CONNECTION_FILE}`, "
+                f"Did you forget to set this in `secrets.toml`, `{SNOWSQL_CONNECTION_FILE}`, "
                 "or as kwargs to `st.experimental_connection`?"
             )
 
diff --git a/lib/streamlit/connections/util.py b/lib/streamlit/connections/util.py
index 7c9ae0118be9..6f1b94264ef7 100644
--- a/lib/streamlit/connections/util.py
+++ b/lib/streamlit/connections/util.py
@@ -12,9 +12,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# NOTE: We won't always be able to import from snowflake.connector.connection so need the
+# `type: ignore` comment below, but that comment will explode if `warn-unused-ignores` is
+# turned on when the package is available. Unfortunately, mypy doesn't provide a good
+# way to configure this at a per-line level :(
+# mypy: no-warn-unused-ignores
 
+
+import configparser
+import os
 from typing import Any, Collection, Dict
 
+SNOWSQL_CONNECTION_FILE = "~/.snowsql/config"
+
 
 def extract_from_dict(
     keys: Collection[str], source_dict: Dict[str, Any]
@@ -40,3 +50,40 @@ def extract_from_dict(
             d[k] = source_dict.pop(k)
 
     return d
+
+
+def load_from_snowsql_config_file(connection_name: str) -> Dict[str, Any]:
+    """Loads the dictionary from snowsql config file."""
+    snowsql_config_file = os.path.expanduser(SNOWSQL_CONNECTION_FILE)
+    if not os.path.exists(snowsql_config_file):
+        return {}
+
+    config = configparser.ConfigParser(inline_comment_prefixes="#")
+    config.read(snowsql_config_file)
+
+    if f"connections.{connection_name}" in config:
+        raw_conn_params = config[f"connections.{connection_name}"]
+    elif "connections" in config:
+        raw_conn_params = config["connections"]
+    else:
+        return {}
+
+    conn_params = {
+        k.replace("name", ""): v.strip('"') for k, v in raw_conn_params.items()
+    }
+
+    if "db" in conn_params:
+        conn_params["database"] = conn_params["db"]
+        del conn_params["db"]
+
+    return conn_params
+
+
+def running_in_sis() -> bool:
+    """Return whether this app seems to be running in SiS."""
+    import snowflake.connector.connection  # type: ignore
+
+    # snowflake.connector.connection.SnowflakeConnection does not exist inside a Stored
+    # Proc or Streamlit. It is only part of the external package. So this returns true
+    # only in SiS.
+    return not hasattr(snowflake.connector.connection, "SnowflakeConnection")
diff --git a/lib/tests/streamlit/connections/snowpark_connection_test.py b/lib/tests/streamlit/connections/snowpark_connection_test.py
index f1a03fd36777..878cc82b10c1 100644
--- a/lib/tests/streamlit/connections/snowpark_connection_test.py
+++ b/lib/tests/streamlit/connections/snowpark_connection_test.py
@@ -14,13 +14,12 @@
 
 import threading
 import unittest
-from unittest.mock import MagicMock, PropertyMock, mock_open, patch
+from unittest.mock import MagicMock, PropertyMock, patch
 
 import pytest
 
 import streamlit as st
 from streamlit.connections import SnowparkConnection
-from streamlit.connections.snowpark_connection import _load_from_snowsql_config_file
 from streamlit.errors import StreamlitAPIException
 from streamlit.runtime.scriptrunner import add_script_run_ctx
 from streamlit.runtime.secrets import AttrDict
@@ -32,59 +31,12 @@ class SnowparkConnectionTest(unittest.TestCase):
     def tearDown(self) -> None:
         st.cache_data.clear()
 
-    def test_load_from_snowsql_config_file_no_file(self):
-        assert _load_from_snowsql_config_file("my_snowpark_connection") == {}
-
-    @patch(
-        "streamlit.connections.snowpark_connection.os.path.exists",
-        MagicMock(return_value=True),
-    )
-    def test_load_from_snowsql_config_file_no_section(self):
-        with patch("builtins.open", new_callable=mock_open, read_data=""):
-            assert _load_from_snowsql_config_file("my_snowpark_connection") == {}
-
-    @patch(
-        "streamlit.connections.snowpark_connection.os.path.exists",
-        MagicMock(return_value=True),
-    )
-    def test_load_from_snowsql_config_file_named_section(self):
-        config_data = """
-[connections.my_snowpark_connection]
-accountname = "hello"
-dbname = notPostgres
-
-[connections]
-accountname = "i get overwritten"
-schemaname = public
-"""
-        with patch("builtins.open", new_callable=mock_open, read_data=config_data):
-            assert _load_from_snowsql_config_file("my_snowpark_connection") == {
-                "account": "hello",
-                "database": "notPostgres",
-            }
-
-    @patch(
-        "streamlit.connections.snowpark_connection.os.path.exists",
-        MagicMock(return_value=True),
-    )
-    def test_load_from_snowsql_config_file_default_section(self):
-        config_data = """
-[connections]
-accountname = "not overwritten"
-schemaname = public
-"""
-        with patch("builtins.open", new_callable=mock_open, read_data=config_data):
-            assert _load_from_snowsql_config_file("my_snowpark_connection") == {
-                "account": "not overwritten",
-                "schema": "public",
-            }
-
     @patch(
         "snowflake.snowpark.context.get_active_session",
         MagicMock(return_value="some active session"),
     )
     @patch(
-        "streamlit.connections.snowpark_connection._running_in_sis",
+        "streamlit.connections.snowpark_connection.running_in_sis",
         MagicMock(return_value=True),
     )
     def test_uses_active_session_if_in_sis(self):
@@ -92,7 +44,7 @@ def test_uses_active_session_if_in_sis(self):
         assert conn._instance == "some active session"
 
     @patch(
-        "streamlit.connections.snowpark_connection._load_from_snowsql_config_file",
+        "streamlit.connections.snowpark_connection.load_from_snowsql_config_file",
         MagicMock(
             return_value={"account": "some_val_1", "password": "i get overwritten"}
         ),
diff --git a/lib/tests/streamlit/connections/util_test.py b/lib/tests/streamlit/connections/util_test.py
index 0de59b1c04a1..6ea00597b4b2 100644
--- a/lib/tests/streamlit/connections/util_test.py
+++ b/lib/tests/streamlit/connections/util_test.py
@@ -13,8 +13,15 @@
 # limitations under the License.
 
 import unittest
+from unittest.mock import MagicMock, mock_open, patch
 
-from streamlit.connections.util import extract_from_dict
+import pytest
+
+from streamlit.connections.util import (
+    extract_from_dict,
+    load_from_snowsql_config_file,
+    running_in_sis,
+)
 
 
 class ConnectionUtilTest(unittest.TestCase):
@@ -28,3 +35,63 @@ def test_extract_from_dict(self):
 
         assert extracted == {"k1": "v1", "k2": "v2"}
         assert d == {"k3": "v3", "k4": "v4"}
+
+    @pytest.mark.require_snowflake
+    @patch("snowflake.connector.connection", MagicMock())
+    def test_not_running_in_sis(self):
+        assert not running_in_sis()
+
+    @pytest.mark.require_snowflake
+    @patch(
+        "snowflake.connector.connection",
+    )
+    def test_running_in_sis(self, patched_connection):
+        delattr(patched_connection, "SnowflakeConnection")
+        assert running_in_sis()
+
+    def test_load_from_snowsql_config_file_no_file(self):
+        assert load_from_snowsql_config_file("my_snowpark_connection") == {}
+
+    @patch(
+        "streamlit.connections.util.os.path.exists",
+        MagicMock(return_value=True),
+    )
+    def test_load_from_snowsql_config_file_no_section(self):
+        with patch("builtins.open", new_callable=mock_open, read_data=""):
+            assert load_from_snowsql_config_file("my_snowpark_connection") == {}
+
+    @patch(
+        "streamlit.connections.util.os.path.exists",
+        MagicMock(return_value=True),
+    )
+    def test_load_from_snowsql_config_file_named_section(self):
+        config_data = """
+[connections.my_snowpark_connection]
+accountname = "hello"
+dbname = notPostgres
+
+[connections]
+accountname = "i get overwritten"
+schemaname = public
+"""
+        with patch("builtins.open", new_callable=mock_open, read_data=config_data):
+            assert load_from_snowsql_config_file("my_snowpark_connection") == {
+                "account": "hello",
+                "database": "notPostgres",
+            }
+
+    @patch(
+        "streamlit.connections.util.os.path.exists",
+        MagicMock(return_value=True),
+    )
+    def test_load_from_snowsql_config_file_default_section(self):
+        config_data = """
+[connections]
+accountname = "not overwritten"
+schemaname = public
+"""
+        with patch("builtins.open", new_callable=mock_open, read_data=config_data):
+            assert load_from_snowsql_config_file("my_snowpark_connection") == {
+                "account": "not overwritten",
+                "schema": "public",
+            }

Note: This work is being done for `feature/st.connection_GA` but is being merged straight
into `develop` to keep the final diff size down.

As we work to replace `SnowparkConnection` with a more general `SnowflakeConnection`,
some of the helper functions that live in `SnowparkConnection` are being reused, so we should
move them into the connections util file.