streamlit-nightly 1.39.1.dev20241029__py2.py3-none-any.whl → 1.39.1.dev20241031__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. streamlit/connections/base_connection.py +4 -0
  2. streamlit/connections/snowflake_connection.py +310 -36
  3. streamlit/connections/snowpark_connection.py +1 -1
  4. streamlit/connections/sql_connection.py +140 -26
  5. streamlit/proto/MetricsEvent_pb2.py +30 -0
  6. streamlit/proto/MetricsEvent_pb2.pyi +200 -0
  7. streamlit/runtime/connection_factory.py +122 -37
  8. streamlit/static/asset-manifest.json +21 -19
  9. streamlit/static/index.html +1 -1
  10. streamlit/static/static/js/1504.51c32670.chunk.js +2 -0
  11. streamlit/static/static/js/223.7f54d4fe.chunk.js +1 -0
  12. streamlit/static/static/js/2601.677fbaac.chunk.js +1 -0
  13. streamlit/static/static/js/3224.919d670d.chunk.js +1 -0
  14. streamlit/static/static/js/3682.8ecb602d.chunk.js +1 -0
  15. streamlit/static/static/js/3978.58bcc6a4.chunk.js +2 -0
  16. streamlit/static/static/js/4133.5d5fe118.chunk.js +2 -0
  17. streamlit/static/static/js/{4942.e4db7877.chunk.js → 4942.cd41b09c.chunk.js} +1 -1
  18. streamlit/static/static/js/5361.913e291a.chunk.js +1 -0
  19. streamlit/static/static/js/5503.0917ff70.chunk.js +1 -0
  20. streamlit/static/static/js/7612.3f409b56.chunk.js +2 -0
  21. streamlit/static/static/js/783.403d1195.chunk.js +1 -0
  22. streamlit/static/static/js/8148.9b76a4cd.chunk.js +1 -0
  23. streamlit/static/static/js/8237.86c539f3.chunk.js +1 -0
  24. streamlit/static/static/js/8380.9d225c39.chunk.js +1 -0
  25. streamlit/static/static/js/8460.570de48a.chunk.js +1 -0
  26. streamlit/static/static/js/9923.14294b15.chunk.js +1 -0
  27. streamlit/static/static/js/9943.ac0530dc.chunk.js +1 -0
  28. streamlit/static/static/js/main.754d974e.js +28 -0
  29. streamlit/web/server/routes.py +1 -0
  30. streamlit/web/server/server.py +6 -1
  31. {streamlit_nightly-1.39.1.dev20241029.dist-info → streamlit_nightly-1.39.1.dev20241031.dist-info}/METADATA +1 -1
  32. {streamlit_nightly-1.39.1.dev20241029.dist-info → streamlit_nightly-1.39.1.dev20241031.dist-info}/RECORD +42 -38
  33. streamlit/static/static/js/223.2359dae5.chunk.js +0 -1
  34. streamlit/static/static/js/3224.a04b49ba.chunk.js +0 -1
  35. streamlit/static/static/js/3389.71902a75.chunk.js +0 -1
  36. streamlit/static/static/js/4096.178414b7.chunk.js +0 -2
  37. streamlit/static/static/js/4297.3afbdd03.chunk.js +0 -2
  38. streamlit/static/static/js/4856.03868952.chunk.js +0 -2
  39. streamlit/static/static/js/5849.e92568db.chunk.js +0 -1
  40. streamlit/static/static/js/6198.956025ac.chunk.js +0 -2
  41. streamlit/static/static/js/6360.50be2ff5.chunk.js +0 -1
  42. streamlit/static/static/js/783.788bb3ab.chunk.js +0 -1
  43. streamlit/static/static/js/8148.b29e43b5.chunk.js +0 -1
  44. streamlit/static/static/js/8237.58ae67da.chunk.js +0 -1
  45. streamlit/static/static/js/8460.8b3f792f.chunk.js +0 -1
  46. streamlit/static/static/js/9923.8dc9e5ed.chunk.js +0 -1
  47. streamlit/static/static/js/9943.51b39296.chunk.js +0 -1
  48. streamlit/static/static/js/main.8c705f66.js +0 -28
  49. /streamlit/static/static/css/{4096.8b8f33d6.chunk.css → 1504.8b8f33d6.chunk.css} +0 -0
  50. /streamlit/static/static/js/{4096.178414b7.chunk.js.LICENSE.txt → 1504.51c32670.chunk.js.LICENSE.txt} +0 -0
  51. /streamlit/static/static/js/{6198.956025ac.chunk.js.LICENSE.txt → 3978.58bcc6a4.chunk.js.LICENSE.txt} +0 -0
  52. /streamlit/static/static/js/{4297.3afbdd03.chunk.js.LICENSE.txt → 4133.5d5fe118.chunk.js.LICENSE.txt} +0 -0
  53. /streamlit/static/static/js/{4856.03868952.chunk.js.LICENSE.txt → 7612.3f409b56.chunk.js.LICENSE.txt} +0 -0
  54. /streamlit/static/static/js/{main.8c705f66.js.LICENSE.txt → main.754d974e.js.LICENSE.txt} +0 -0
  55. {streamlit_nightly-1.39.1.dev20241029.data → streamlit_nightly-1.39.1.dev20241031.data}/scripts/streamlit.cmd +0 -0
  56. {streamlit_nightly-1.39.1.dev20241029.dist-info → streamlit_nightly-1.39.1.dev20241031.dist-info}/WHEEL +0 -0
  57. {streamlit_nightly-1.39.1.dev20241029.dist-info → streamlit_nightly-1.39.1.dev20241031.dist-info}/entry_points.txt +0 -0
  58. {streamlit_nightly-1.39.1.dev20241029.dist-info → streamlit_nightly-1.39.1.dev20241031.dist-info}/top_level.txt +0 -0
@@ -154,6 +154,10 @@ class BaseConnection(ABC, Generic[RawConnectionT]):
154
154
  reinitializing it. Note that some connection methods may already use ``reset()``
155
155
  in their error handling code.
156
156
 
157
+ Returns
158
+ -------
159
+ None
160
+
157
161
  Example
158
162
  -------
159
163
  >>> import streamlit as st
@@ -40,14 +40,159 @@ if TYPE_CHECKING:
40
40
 
41
41
 
42
42
  class SnowflakeConnection(BaseConnection["InternalSnowflakeConnection"]):
43
- """A connection to Snowflake using the Snowflake Python Connector. Initialize using
44
- ``st.connection("<name>", type="snowflake")``.
45
-
46
- SnowflakeConnection supports direct SQL querying using ``.query("...")``, access to
47
- the underlying Snowflake Python Connector object with ``.raw_connection``, and other
48
- convenience functions. See the methods below for more information.
49
- SnowflakeConnections should always be created using ``st.connection()``, **not**
50
- initialized directly.
43
+ """A connection to Snowflake using the Snowflake Connector for Python.
44
+
45
+ Initialize this connection object using ``st.connection("snowflake")`` or
46
+ ``st.connection("<name>", type="snowflake")``. Connection parameters for a
47
+ SnowflakeConnection can be specified using ``secrets.toml`` and/or
48
+ ``**kwargs``. Connection parameters are passed to
49
+ |snowflake.connector.connect()|.
50
+
51
+ When an app is running in Streamlit in Snowflake,
52
+ ``st.connection("snowflake")`` connects automatically using the app owner's
53
+ role without further configuration. ``**kwargs`` will be ignored in this
54
+ case. Use ``secrets.toml`` and ``**kwargs`` to configure your connection
55
+ for local development.
56
+
57
+ SnowflakeConnection includes several convenience methods. For example, you
58
+ can directly execute a SQL query with ``.query()`` or access the underlying
59
+ Snowflake Connector object with ``.raw_connection``.
60
+
61
+ .. |snowflake.connector.connect()| replace:: ``snowflake.connector.connect()``
62
+ .. _snowflake.connector.connect(): https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#label-snowflake-connector-methods-connect
63
+
64
+ .. Tip::
65
+ `snowflake-snowpark-python <https://pypi.org/project/snowflake-snowpark-python/>`_
66
+ must be installed in your environment to use this connection. You can
67
+ install Snowflake extras along with Streamlit:
68
+
69
+ >>> pip install streamlit[snowflake]
70
+
71
+ .. Important::
72
+ Account identifiers must be of the form ``<orgname>-<account_name>``
73
+ where ``<orgname>`` is the name of your Snowflake organization and
74
+ ``<account_name>`` is the unique name of your account within your
75
+ organization. This is dash-separated, not dot-separated like when used
76
+ in SQL queries. For more information, see `Account identifiers
77
+ <https://docs.snowflake.com/en/user-guide/admin-account-identifier>`_.
78
+
79
+ Examples
80
+ --------
81
+
82
+ **Example 1: Configuration with Streamlit secrets**
83
+
84
+ You can configure your Snowflake connection using Streamlit's
85
+ `Secrets management <https://docs.streamlit.io/develop/concepts/connections/secrets-management>`_.
86
+ For example, if you have MFA enabled on your account, you can connect using
87
+ `key-pair authentication <https://docs.snowflake.com/en/user-guide/key-pair-auth>`_.
88
+
89
+ ``.streamlit/secrets.toml``:
90
+
91
+ >>> [connections.snowflake]
92
+ >>> account = "xxx-xxx"
93
+ >>> user = "xxx"
94
+ >>> private_key_file = "/xxx/xxx/xxx.p8"
95
+ >>> role = "xxx"
96
+ >>> warehouse = "xxx"
97
+ >>> database = "xxx"
98
+ >>> schema = "xxx"
99
+
100
+ Your app code:
101
+
102
+ >>> import streamlit as st
103
+ >>> conn = st.connection("snowflake")
104
+ >>> df = conn.query("SELECT * FROM my_table")
105
+
106
+ **Example 2: Configuration with keyword arguments and external authentication**
107
+
108
+ You can configure your Snowflake connection with keyword arguments (with or
109
+ without ``secrets.toml``). For example, if your Snowflake account supports
110
+ SSO, you can set up a quick local connection for development using `browser-based SSO
111
+ <https://docs.snowflake.com/en/user-guide/admin-security-fed-auth-use#how-browser-based-sso-works>`_.
112
+
113
+ >>> import streamlit as st
114
+ >>> conn = st.connection(
115
+ ... "snowflake", account="xxx-xxx", user="xxx", authenticator="externalbrowser"
116
+ ... )
117
+ >>> df = conn.query("SELECT * FROM my_table")
118
+
119
+ **Example 3: Named connection with Snowflake's connection configuration file**
120
+
121
+ Snowflake's Python Connector supports a `connection configuration file
122
+ <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#connecting-using-the-connections-toml-file>`_,
123
+ which is well integrated with Streamlit's ``SnowflakeConnection``. If you
124
+ already have one or more connections configured, all you need to do is pass
125
+ the name of the connection to use.
126
+
127
+ ``~/.snowflake/connections.toml``:
128
+
129
+ >>> [my_connection]
130
+ >>> account = "xxx-xxx"
131
+ >>> user = "xxx"
132
+ >>> password = "xxx"
133
+ >>> warehouse = "xxx"
134
+ >>> database = "xxx"
135
+ >>> schema = "xxx"
136
+
137
+ Your app code:
138
+
139
+ >>> import streamlit as st
140
+ >>> conn = st.connection("my_connection", type="snowflake")
141
+ >>> df = conn.query("SELECT * FROM my_table")
142
+
143
+ **Example 4: Named connection with Streamlit secrets and Snowflake's connection configuration file**
144
+
145
+ If you have a Snowflake configuration file with a connection named
146
+ ``my_connection`` as in Example 3, you can pass the connection name through
147
+ ``secrets.toml``.
148
+
149
+ ``.streamlit/secrets.toml``:
150
+
151
+ >>> [connections.snowflake]
152
+ >>> connection_name = "my_connection"
153
+
154
+ Your app code:
155
+
156
+ >>> import streamlit as st
157
+ >>> conn = st.connection("snowflake")
158
+ >>> df = conn.query("SELECT * FROM my_table")
159
+
160
+ **Example 5: Default connection with an environment variable**
161
+
162
+ If you have a Snowflake configuration file with a connection named
163
+ ``my_connection`` as in Example 3, you can set an environment variable to
164
+ declare it as the default Snowflake connection.
165
+
166
+ >>> SNOWFLAKE_DEFAULT_CONNECTION_NAME = "my_connection"
167
+
168
+ Your app code:
169
+
170
+ >>> import streamlit as st
171
+ >>> conn = st.connection("snowflake")
172
+ >>> df = conn.query("SELECT * FROM my_table")
173
+
174
+ **Example 6: Default connection in Snowflake's connection configuration file**
175
+
176
+ If you have a Snowflake configuration file that defines your ``default``
177
+ connection, Streamlit will automatically use it if no other connection is
178
+ declared.
179
+
180
+ ``~/.snowflake/connections.toml``:
181
+
182
+ >>> [default]
183
+ >>> account = "xxx-xxx"
184
+ >>> user = "xxx"
185
+ >>> password = "xxx"
186
+ >>> warehouse = "xxx"
187
+ >>> database = "xxx"
188
+ >>> schema = "xxx"
189
+
190
+ Your app code:
191
+
192
+ >>> import streamlit as st
193
+ >>> conn = st.connection("snowflake")
194
+ >>> df = conn.query("SELECT * FROM my_table")
195
+
51
196
  """
52
197
 
53
198
  def _connect(self, **kwargs) -> InternalSnowflakeConnection:
@@ -129,29 +274,37 @@ class SnowflakeConnection(BaseConnection["InternalSnowflakeConnection"]):
129
274
  ) -> DataFrame:
130
275
  """Run a read-only SQL query.
131
276
 
132
- This method implements both query result caching (with caching behavior
133
- identical to that of using ``@st.cache_data``) as well as simple error handling/retries.
277
+ This method implements query result caching and simple error
278
+ handling/retries. The caching behavior is identical to that of using
279
+ ``@st.cache_data``.
134
280
 
135
281
  .. note::
136
- Queries that are run without a specified ttl are cached indefinitely.
282
+ Queries that are run without a specified ``ttl`` are cached
283
+ indefinitely.
137
284
 
138
285
  Parameters
139
286
  ----------
140
287
  sql : str
141
288
  The read-only SQL query to execute.
142
289
  ttl : float, int, timedelta or None
143
- The maximum number of seconds to keep results in the cache, or
144
- None if cached results should not expire. The default is None.
290
+ The maximum number of seconds to keep results in the cache. If this
291
+ is ``None`` (default), cached results do not expire with time.
145
292
  show_spinner : boolean or string
146
- Enable the spinner. The default is to show a spinner when there is a
147
- "cache miss" and the cached resource is being created. If a string, the value
148
- of the show_spinner param will be used for the spinner text.
293
+ Whether to enable the spinner. When a cached query is executed, no
294
+ spinner is displayed because the result is immediately available.
295
+ When a new query is executed, the default is to show a spinner with
296
+ the message "Running ``snowflake.query(...)``."
297
+
298
+ If this is ``False``, no spinner displays while executing the
299
+ query. If this is a string, the string will be used as the message
300
+ for the spinner.
149
301
  params : list, tuple, dict or None
150
- List of parameters to pass to the execute method. This connector supports
151
- binding data to a SQL statement using qmark bindings. For more information
152
- and examples, see the `Snowflake Python Connector documentation
302
+ List of parameters to pass to the Snowflake Connector for Python
303
+ ``Cursor.execute()`` method. This connector supports binding data
304
+ to a SQL statement using qmark bindings. For more information and
305
+ examples, see the `Snowflake Connector for Python documentation
153
306
  <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example#using-qmark-or-numeric-binding>`_.
154
- Default is None.
307
+ This defaults to ``None``.
155
308
 
156
309
  Returns
157
310
  -------
@@ -160,11 +313,13 @@ class SnowflakeConnection(BaseConnection["InternalSnowflakeConnection"]):
160
313
 
161
314
  Example
162
315
  -------
316
+
163
317
  >>> import streamlit as st
164
318
  >>>
165
319
  >>> conn = st.connection("snowflake")
166
- >>> df = conn.query("select * from pet_owners")
320
+ >>> df = conn.query("SELECT * FROM my_table")
167
321
  >>> st.dataframe(df)
322
+
168
323
  """
169
324
  from snowflake.connector.errors import ProgrammingError # type: ignore[import]
170
325
  from snowflake.connector.network import ( # type: ignore[import]
@@ -232,20 +387,64 @@ class SnowflakeConnection(BaseConnection["InternalSnowflakeConnection"]):
232
387
  chunk_size: int | None = None,
233
388
  **kwargs,
234
389
  ) -> tuple[bool, int, int]:
235
- """Call snowflake.connector.pandas_tools.write_pandas with this connection.
390
+ """Write a ``pandas.DataFrame`` to a table in a Snowflake database.
236
391
 
237
- This convenience method is simply a thin wrapper around the ``write_pandas``
238
- function of the same name from ``snowflake.connector.pandas_tools``. For more
239
- information, see the `Snowflake Python Connector documentation
392
+ This convenience method is a thin wrapper around
393
+ ``snowflake.connector.pandas_tools.write_pandas()`` using the
394
+ underlying connection. The ``conn`` parameter is passed automatically.
395
+ For more information and additional keyword arguments, see the
396
+ `Snowflake Connector for Python documentation
240
397
  <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#write_pandas>`_.
241
398
 
399
+ Parameters
400
+ ----------
401
+ df: pandas.DataFrame
402
+ The ``pandas.DataFrame`` object containing the data to be copied
403
+ into the table.
404
+ table_name: str
405
+ Name of the table where the data should be copied to.
406
+ database: str
407
+ Name of the database containing the table. By default, the function
408
+ writes to the database that is currently in use in the session.
409
+
410
+ .. Note::
411
+ If you specify this parameter, you must also specify the schema
412
+ parameter.
413
+
414
+ schema: str
415
+ Name of the schema containing the table. By default, the function
416
+ writes to the table in the schema that is currently in use in the
417
+ session.
418
+ chunk_size: int
419
+ Number of elements to insert at a time. By default, the function
420
+ inserts all elements in one chunk.
421
+ **kwargs: Any
422
+ Additional keyword arguments for
423
+ ``snowflake.connector.pandas_tools.write_pandas()``.
424
+
242
425
  Returns
243
426
  -------
244
427
  tuple[bool, int, int]
245
428
  A tuple containing three values:
246
- 1. A bool that is True if the write was successful.
247
- 2. An int giving the number of chunks of data that were copied.
248
- 3. An int giving the number of rows that were inserted.
429
+
430
+ 1. A boolean value that is ``True`` if the write was successful.
431
+ 2. An integer giving the number of chunks of data that were copied.
432
+ 3. An integer giving the number of rows that were inserted.
433
+
434
+ Example
435
+ -------
436
+ The following example uses the database and schema currently in use in
437
+ the session and copies the data into a table named "my_table."
438
+
439
+ >>> import streamlit as st
440
+ >>> import pandas as pd
441
+ >>>
442
+ >>> df = pd.DataFrame(
443
+ ... {"Name": ["Mary", "John", "Robert"], "Pet": ["dog", "cat", "bird"]}
444
+ ... )
445
+ >>> conn = st.connection("snowflake")
446
+ >>> conn.write_pandas(df, "my_table")
447
+
249
448
  """
250
449
  from snowflake.connector.pandas_tools import write_pandas # type:ignore[import]
251
450
 
@@ -262,27 +461,102 @@ class SnowflakeConnection(BaseConnection["InternalSnowflakeConnection"]):
262
461
  return (success, nchunks, nrows)
263
462
 
264
463
  def cursor(self) -> SnowflakeCursor:
265
- """Return a PEP 249-compliant cursor object.
464
+ """Create a new cursor object from this connection.
266
465
 
267
- For more information, see the `Snowflake Python Connector documentation
466
+ Snowflake Connector cursors implement the Python Database API v2.0
467
+ specification (PEP-249). For more information, see the
468
+ `Snowflake Connector for Python documentation
268
469
  <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-api#object-cursor>`_.
470
+
471
+ Returns
472
+ -------
473
+ snowflake.connector.cursor.SnowflakeCursor
474
+ A cursor object for the connection.
475
+
476
+ Example
477
+ -------
478
+ The following example uses a cursor to insert multiple rows into a
479
+ table. The ``qmark`` parameter style is specified as an optional
480
+ keyword argument. Alternatively, the parameter style can be declared in
481
+ your connection configuration file. For more information, see the
482
+ `Snowflake Connector for Python documentation
483
+ <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example#using-qmark-or-numeric-binding>`_.
484
+
485
+ >>> import streamlit as st
486
+ >>>
487
+ >>> conn = st.connection("snowflake", "paramstyle"="qmark")
488
+ >>> rows_to_insert = [("Mary", "dog"), ("John", "cat"), ("Robert", "bird")]
489
+ >>> conn.cursor().executemany(
490
+ ... "INSERT INTO mytable (name, pet) VALUES (?, ?)", rows_to_insert
491
+ ... )
492
+
269
493
  """
270
494
  return self._instance.cursor()
271
495
 
272
496
  @property
273
497
  def raw_connection(self) -> InternalSnowflakeConnection:
274
- """Access the underlying Snowflake Python connector object.
498
+ """Access the underlying connection object from the Snowflake\
499
+ Connector for Python.
500
+
501
+ For information on how to use the Snowflake Connector for Python, see
502
+ the `Snowflake Connector for Python documentation
503
+ <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example>`_.
504
+
505
+ Returns
506
+ -------
507
+ snowflake.connector.connection.SnowflakeConnection
508
+ The connection object.
509
+
510
+ Example
511
+ -------
512
+ The following example uses a cursor to submit an asynchronous query,
513
+ saves the query ID, then periodically checks the query status through
514
+ the connection before retrieving the results.
515
+
516
+ >>> import streamlit as st
517
+ >>> import time
518
+ >>>
519
+ >>> conn = st.connection("snowflake")
520
+ >>> cur = conn.cursor()
521
+ >>> cur.execute_async("SELECT * FROM my_table")
522
+ >>> query_id = cur.sfqid
523
+ >>> while True:
524
+ ... status = conn.raw_connection.get_query_status(query_id)
525
+ ... if conn.raw_connection.is_still_running(status):
526
+ ... time.sleep(1)
527
+ ... else:
528
+ ... break
529
+ >>> cur.get_results_from_sfqid(query_id)
530
+ >>> df = cur.fetchall()
275
531
 
276
- Information on how to use the Snowflake Python Connector can be found in the
277
- `Snowflake Python Connector documentation <https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-example>`_.
278
532
  """
279
533
  return self._instance
280
534
 
281
535
  def session(self) -> Session:
282
- """Create a new Snowpark Session from this connection.
536
+ """Create a new Snowpark session from this connection.
537
+
538
+ For information on how to use Snowpark sessions, see the
539
+ `Snowpark developer guide
540
+ <https://docs.snowflake.com/en/developer-guide/snowpark/python/working-with-dataframes>`_
541
+ and `Snowpark API Reference
542
+ <https://docs.snowflake.com/en/developer-guide/snowpark/reference/python/latest/snowpark/session>`_.
543
+
544
+ Returns
545
+ -------
546
+ snowflake.snowpark.Session
547
+ A new Snowpark session for this connection.
548
+
549
+ Example
550
+ -------
551
+ The following example creates a new Snowpark session and uses it to run
552
+ a query.
553
+
554
+ >>> import streamlit as st
555
+ >>>
556
+ >>> conn = st.connection("snowflake")
557
+ >>> session = conn.session()
558
+ >>> df = session.sql("SELECT * FROM my_table").collect()
283
559
 
284
- Information on how to use Snowpark sessions can be found in the `Snowpark documentation
285
- <https://docs.snowflake.com/en/developer-guide/snowpark/python/working-with-dataframes>`_.
286
560
  """
287
561
  from snowflake.snowpark.context import get_active_session # type:ignore[import]
288
562
  from snowflake.snowpark.session import Session # type:ignore[import]
@@ -123,7 +123,7 @@ class SnowparkConnection(BaseConnection["Session"]):
123
123
  >>> import streamlit as st
124
124
  >>>
125
125
  >>> conn = st.connection("snowpark")
126
- >>> df = conn.query("select * from pet_owners")
126
+ >>> df = conn.query("SELECT * FROM pet_owners")
127
127
  >>> st.dataframe(df)
128
128
  """
129
129
  from snowflake.snowpark.exceptions import ( # type:ignore[import]
@@ -52,35 +52,129 @@ _REQUIRED_CONNECTION_PARAMS = {"dialect", "username", "host"}
52
52
 
53
53
 
54
54
  class SQLConnection(BaseConnection["Engine"]):
55
- """A connection to a SQL database using a SQLAlchemy Engine. Initialize using ``st.connection("<name>", type="sql")``.
55
+ """A connection to a SQL database using a SQLAlchemy Engine.
56
+
57
+ Initialize this connection object using ``st.connection("sql")`` or
58
+ ``st.connection("<name>", type="sql")``. Connection parameters for a
59
+ SQLConnection can be specified using ``secrets.toml`` and/or ``**kwargs``.
60
+ Possible connection parameters include:
61
+
62
+ - ``url`` or keyword arguments for |sqlalchemy.engine.URL.create()|_, except
63
+ ``drivername``. Use ``dialect`` and ``driver`` instead of ``drivername``.
64
+ - Keyword arguments for |sqlalchemy.create_engine()|_, including custom
65
+ ``connect()`` arguments used by your specific ``dialect`` or ``driver``.
66
+ - ``autocommit``. If this is ``False`` (default), the connection operates
67
+ in manual commit (transactional) mode. If this is ``True``, the
68
+ connection operates in autocommit (non-transactional) mode.
69
+
70
+ If ``url`` exists as a connection parameter, Streamlit will pass it to
71
+ ``sqlalchemy.engine.make_url()``. Otherwise, Streamlit requires (at a
72
+ minimum) ``dialect``, ``username``, and ``host``. Streamlit will use
73
+ ``dialect`` and ``driver`` (if defined) to derive ``drivername``, then pass
74
+ the relevant connection parameters to ``sqlalchemy.engine.URL.create()``.
75
+
76
+ In addition to the default keyword arguments for ``sqlalchemy.create_engine()``,
77
+ your dialect may accept additional keyword arguments. For example, if you
78
+ use ``dialect="snowflake"`` with `Snowflake SQLAlchemy
79
+ <https://github.com/snowflakedb/snowflake-sqlalchemy#key-pair-authentication-support>`_,
80
+ you can pass a value for ``private_key`` to use key-pair authentication. If
81
+ you use ``dialect="bigquery"`` with `Google BigQuery
82
+ <https://github.com/googleapis/python-bigquery-sqlalchemy#authentication>`_,
83
+ you can pass a value for ``location``.
84
+
85
+ SQLConnection provides the ``.query()`` convenience method, which can be
86
+ used to run simple, read-only queries with both caching and simple error
87
+ handling/retries. More complex database interactions can be performed by
88
+ using the ``.session`` property to receive a regular SQLAlchemy Session.
89
+
90
+ .. Important::
91
+ `SQLAlchemy <https://pypi.org/project/SQLAlchemy/>`_ must be installed
92
+ in your environment to use this connection. You must also install your
93
+ driver, such as ``pyodbc`` or ``psycopg2``.
94
+
95
+ .. |sqlalchemy.engine.URL.create()| replace:: ``sqlalchemy.engine.URL.create()``
96
+ .. _sqlalchemy.engine.URL.create(): https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.engine.URL.create
97
+ .. |sqlalchemy.engine.make_url()| replace:: ``sqlalchemy.engine.make_url()``
98
+ .. _sqlalchemy.engine.make_url(): https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.engine.make_url
99
+ .. |sqlalchemy.create_engine()| replace:: ``sqlalchemy.create_engine()``
100
+ .. _sqlalchemy.create_engine(): https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.create_engine
101
+
102
+ Examples
103
+ --------
104
+
105
+ **Example 1: Configuration with URL**
106
+
107
+ You can configure your SQL connection using Streamlit's
108
+ `Secrets management <https://docs.streamlit.io/develop/concepts/connections/secrets-management>`_.
109
+ The following example specifies a SQL connection URL.
110
+
111
+ ``.streamlit/secrets.toml``:
112
+
113
+ >>> [connections.sql]
114
+ >>> url = "xxx+xxx://xxx:xxx@xxx:xxx/xxx"
115
+
116
+ Your app code:
56
117
 
57
- SQLConnection provides the ``query()`` convenience method, which can be used to
58
- run simple read-only queries with both caching and simple error handling/retries.
59
- More complex DB interactions can be performed by using the ``.session`` property
60
- to receive a regular SQLAlchemy Session.
118
+ >>> import streamlit as st
119
+ >>>
120
+ >>> conn = st.connection("sql")
121
+ >>> df = conn.query("SELECT * FROM pet_owners")
122
+ >>> st.dataframe(df)
61
123
 
62
- SQLConnections should always be created using ``st.connection()``, **not**
63
- initialized directly. Connection parameters for a SQLConnection can be specified
64
- using either ``st.secrets`` or ``**kwargs``. Some frequently used parameters include:
124
+ **Example 2: Configuration with dialect, host, and username**
65
125
 
66
- - **url** or arguments for `sqlalchemy.engine.URL.create()
67
- <https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.engine.URL.create>`_.
68
- Most commonly it includes a dialect, host, database, username and password.
126
+ If you do not specify ``url``, you must at least specify ``dialect``,
127
+ ``host``, and ``username`` instead. The following example also includes
128
+ ``password``.
69
129
 
70
- - **create_engine_kwargs** can be passed via ``st.secrets``, such as for
71
- `snowflake-sqlalchemy <https://github.com/snowflakedb/snowflake-sqlalchemy#key-pair-authentication-support>`_
72
- or `Google BigQuery <https://github.com/googleapis/python-bigquery-sqlalchemy#authentication>`_.
73
- These can also be passed directly as ``**kwargs`` to connection().
130
+ ``.streamlit/secrets.toml``:
74
131
 
75
- - **autocommit=True** to run with isolation level ``AUTOCOMMIT``. Default is False.
132
+ >>> [connections.sql]
133
+ >>> dialect = "xxx"
134
+ >>> host = "xxx"
135
+ >>> username = "xxx"
136
+ >>> password = "xxx"
137
+
138
+ Your app code:
76
139
 
77
- Example
78
- -------
79
140
  >>> import streamlit as st
80
141
  >>>
81
142
  >>> conn = st.connection("sql")
82
- >>> df = conn.query("select * from pet_owners")
143
+ >>> df = conn.query("SELECT * FROM pet_owners")
144
+ >>> st.dataframe(df)
145
+
146
+ **Example 3: Configuration with keyword arguments**
147
+
148
+ You can configure your SQL connection with keyword arguments (with or
149
+ without ``secrets.toml``). For example, if you use Microsoft Entra ID with
150
+ a Microsoft Azure SQL server, you can quickly set up a local connection for
151
+ development using `interactive authentication
152
+ <https://learn.microsoft.com/en-us/sql/connect/odbc/using-azure-active-directory?view=sql-server-ver16#new-andor-modified-dsn-and-connection-string-keywords>`_.
153
+
154
+ This example requires the `Microsoft ODBC Driver for SQL Server
155
+ <https://learn.microsoft.com/en-us/sql/connect/odbc/microsoft-odbc-driver-for-sql-server?view=sql-server-ver16>`_
156
+ for *Windows* in addition to the ``sqlalchemy`` and ``pyodbc`` packages for
157
+ Python.
158
+
159
+ >>> import streamlit as st
160
+ >>>
161
+ >>> conn = st.connection(
162
+ ... "sql",
163
+ ... dialect="mssql",
164
+ ... driver="pyodbc",
165
+ ... host="xxx.database.windows.net",
166
+ ... database="xxx",
167
+ ... username="xxx",
168
+ ... query={
169
+ ... "driver": "ODBC Driver 18 for SQL Server",
170
+ ... "authentication": "ActiveDirectoryInteractive",
171
+ ... "encrypt": "yes",
172
+ ... },
173
+ ... )
174
+ >>>
175
+ >>> df = conn.query("SELECT * FROM pet_owners")
83
176
  >>> st.dataframe(df)
177
+
84
178
  """
85
179
 
86
180
  def _connect(self, autocommit: bool = False, **kwargs) -> Engine:
@@ -140,15 +234,15 @@ class SQLConnection(BaseConnection["Engine"]):
140
234
  ) -> DataFrame:
141
235
  """Run a read-only query.
142
236
 
143
- This method implements both query result caching (with caching behavior
144
- identical to that of using ``@st.cache_data``) as well as simple error handling/retries.
237
+ This method implements query result caching and simple error
238
+ handling/retries. The caching behavior is identical to that of using
239
+ ``@st.cache_data``.
145
240
 
146
241
  .. note::
147
242
  Queries that are run without a specified ttl are cached indefinitely.
148
243
 
149
- Aside from the ``ttl`` kwarg, all kwargs passed to this function are passed down
150
- to |pandas.read_sql|_
151
- and have the behavior described in the pandas documentation.
244
+ All keyword arguments passed to this function are passed down to
245
+ |pandas.read_sql|_, except ``ttl``.
152
246
 
153
247
  .. |pandas.read_sql| replace:: ``pandas.read_sql``
154
248
  .. _pandas.read_sql: https://pandas.pydata.org/docs/reference/api/pandas.read_sql.html
@@ -192,7 +286,7 @@ class SQLConnection(BaseConnection["Engine"]):
192
286
  >>>
193
287
  >>> conn = st.connection("sql")
194
288
  >>> df = conn.query(
195
- ... "select * from pet_owners where owner = :owner",
289
+ ... "SELECT * FROM pet_owners WHERE owner = :owner",
196
290
  ... ttl=3600,
197
291
  ... params={"owner": "barbara"},
198
292
  ... )
@@ -259,12 +353,17 @@ class SQLConnection(BaseConnection["Engine"]):
259
353
 
260
354
  def connect(self) -> SQLAlchemyConnection:
261
355
  """Call ``.connect()`` on the underlying SQLAlchemy Engine, returning a new\
262
- ``sqlalchemy.engine.Connection`` object.
356
+ connection object.
263
357
 
264
358
  Calling this method is equivalent to calling ``self._instance.connect()``.
265
359
 
266
360
  NOTE: This method should not be confused with the internal ``_connect`` method used
267
361
  to implement a Streamlit Connection.
362
+
363
+ Returns
364
+ -------
365
+ sqlalchemy.engine.Connection
366
+ A new SQLAlchemy connection object.
268
367
  """
269
368
  return self._instance.connect()
270
369
 
@@ -273,6 +372,11 @@ class SQLConnection(BaseConnection["Engine"]):
273
372
  """The underlying SQLAlchemy Engine.
274
373
 
275
374
  This is equivalent to accessing ``self._instance``.
375
+
376
+ Returns
377
+ -------
378
+ sqlalchemy.engine.base.Engine
379
+ The underlying SQLAlchemy Engine.
276
380
  """
277
381
  return self._instance
278
382
 
@@ -281,6 +385,11 @@ class SQLConnection(BaseConnection["Engine"]):
281
385
  """The name of the driver used by the underlying SQLAlchemy Engine.
282
386
 
283
387
  This is equivalent to accessing ``self._instance.driver``.
388
+
389
+ Returns
390
+ -------
391
+ str
392
+ The name of the driver. For example, ``"pyodbc"`` or ``"psycopg2"``.
284
393
  """
285
394
  return cast(str, self._instance.driver)
286
395
 
@@ -296,6 +405,11 @@ class SQLConnection(BaseConnection["Engine"]):
296
405
  <https://docs.sqlalchemy.org/en/20/orm/session_basics.html>`_ docs also contain
297
406
  much more information on the usage of sessions.
298
407
 
408
+ Returns
409
+ -------
410
+ sqlalchemy.orm.Session
411
+ A SQLAlchemy Session.
412
+
299
413
  Example
300
414
  -------
301
415
  >>> import streamlit as st