|
47 | 47 | from pandas.util.version import Version
|
48 | 48 |
|
49 | 49 |
|
50 |
| -class SQLAlchemyRequired(ImportError): |
51 |
| - pass |
52 |
| - |
53 |
| - |
54 | 50 | class DatabaseError(IOError):
|
55 | 51 | pass
|
56 | 52 |
|
57 | 53 |
|
58 | 54 | # -----------------------------------------------------------------------------
|
59 | 55 | # -- Helper functions
|
60 | 56 |
|
61 |
| -_SQLALCHEMY_INSTALLED: bool | None = None |
62 |
| - |
63 |
| - |
64 |
| -def _is_sqlalchemy_connectable(con): |
65 |
| - global _SQLALCHEMY_INSTALLED |
66 |
| - if _SQLALCHEMY_INSTALLED is None: |
67 |
| - try: |
68 |
| - import sqlalchemy |
69 |
| - |
70 |
| - _SQLALCHEMY_INSTALLED = True |
71 |
| - except ImportError: |
72 |
| - _SQLALCHEMY_INSTALLED = False |
73 |
| - |
74 |
| - if _SQLALCHEMY_INSTALLED: |
75 |
| - import sqlalchemy # noqa: F811 |
76 |
| - |
77 |
| - return isinstance(con, sqlalchemy.engine.Connectable) |
78 |
| - else: |
79 |
| - return False |
80 |
| - |
81 | 57 |
|
82 | 58 | def _gt14() -> bool:
|
83 | 59 | """
|
@@ -303,21 +279,14 @@ def read_sql_table(
|
303 | 279 | --------
|
304 | 280 | >>> pd.read_sql_table('table_name', 'postgres:///db_name') # doctest:+SKIP
|
305 | 281 | """
|
306 |
| - con = _engine_builder(con) |
307 |
| - if not _is_sqlalchemy_connectable(con): |
308 |
| - raise NotImplementedError( |
309 |
| - "read_sql_table only supported for SQLAlchemy connectable." |
310 |
| - ) |
311 |
| - import sqlalchemy |
312 |
| - from sqlalchemy.schema import MetaData |
| 282 | + from sqlalchemy.exc import InvalidRequestError |
313 | 283 |
|
314 |
| - meta = MetaData(con, schema=schema) |
| 284 | + pandas_sql = pandasSQL_builder(con, schema=schema) |
315 | 285 | try:
|
316 |
| - meta.reflect(only=[table_name], views=True) |
317 |
| - except sqlalchemy.exc.InvalidRequestError as err: |
| 286 | + pandas_sql.meta.reflect(only=[table_name], views=True) |
| 287 | + except InvalidRequestError as err: |
318 | 288 | raise ValueError(f"Table {table_name} not found") from err
|
319 | 289 |
|
320 |
| - pandas_sql = SQLDatabase(con, meta=meta) |
321 | 290 | table = pandas_sql.read_table(
|
322 | 291 | table_name,
|
323 | 292 | index_col=index_col,
|
@@ -752,37 +721,29 @@ def has_table(table_name: str, con, schema: str | None = None):
|
752 | 721 | table_exists = has_table
|
753 | 722 |
|
754 | 723 |
|
755 |
| -def _engine_builder(con): |
756 |
| - """ |
757 |
| - Returns a SQLAlchemy engine from a URI (if con is a string) |
758 |
| - else it just return con without modifying it. |
759 |
| - """ |
760 |
| - global _SQLALCHEMY_INSTALLED |
761 |
| - if isinstance(con, str): |
762 |
| - try: |
763 |
| - import sqlalchemy |
764 |
| - except ImportError: |
765 |
| - _SQLALCHEMY_INSTALLED = False |
766 |
| - else: |
767 |
| - con = sqlalchemy.create_engine(con) |
768 |
| - return con |
769 |
| - |
770 |
| - return con |
771 |
| - |
772 |
| - |
773 |
| -def pandasSQL_builder(con, schema: str | None = None, meta=None): |
| 724 | +def pandasSQL_builder(con, schema: str | None = None): |
774 | 725 | """
|
775 | 726 | Convenience function to return the correct PandasSQL subclass based on the
|
776 | 727 | provided parameters.
|
777 | 728 | """
|
778 |
| - con = _engine_builder(con) |
779 |
| - if _is_sqlalchemy_connectable(con): |
780 |
| - return SQLDatabase(con, schema=schema, meta=meta) |
781 |
| - elif isinstance(con, str): |
782 |
| - raise ImportError("Using URI string without sqlalchemy installed.") |
783 |
| - else: |
| 729 | + import sqlite3 |
| 730 | + |
| 731 | + if isinstance(con, sqlite3.Connection) or con is None: |
784 | 732 | return SQLiteDatabase(con)
|
785 | 733 |
|
| 734 | + sqlalchemy = import_optional_dependency("sqlalchemy") |
| 735 | + |
| 736 | + if isinstance(con, str): |
| 737 | + con = sqlalchemy.create_engine(con) |
| 738 | + |
| 739 | + if isinstance(con, sqlalchemy.engine.Connectable): |
| 740 | + return SQLDatabase(con, schema=schema) |
| 741 | + |
| 742 | + raise ValueError( |
| 743 | + "pandas only support SQLAlchemy connectable(engine/connection) or" |
| 744 | + "database string URI or sqlite3 DBAPI2 connection" |
| 745 | + ) |
| 746 | + |
786 | 747 |
|
787 | 748 | class SQLTable(PandasObject):
|
788 | 749 | """
|
@@ -1387,21 +1348,14 @@ class SQLDatabase(PandasSQL):
|
1387 | 1348 | schema : string, default None
|
1388 | 1349 | Name of SQL schema in database to write to (if database flavor
|
1389 | 1350 | supports this). If None, use default schema (default).
|
1390 |
| - meta : SQLAlchemy MetaData object, default None |
1391 |
| - If provided, this MetaData object is used instead of a newly |
1392 |
| - created. This allows to specify database flavor specific |
1393 |
| - arguments in the MetaData object. |
1394 | 1351 |
|
1395 | 1352 | """
|
1396 | 1353 |
|
1397 |
| - def __init__(self, engine, schema: str | None = None, meta=None): |
1398 |
| - self.connectable = engine |
1399 |
| - if not meta: |
1400 |
| - from sqlalchemy.schema import MetaData |
1401 |
| - |
1402 |
| - meta = MetaData(self.connectable, schema=schema) |
| 1354 | + def __init__(self, engine, schema: str | None = None): |
| 1355 | + from sqlalchemy.schema import MetaData |
1403 | 1356 |
|
1404 |
| - self.meta = meta |
| 1357 | + self.connectable = engine |
| 1358 | + self.meta = MetaData(self.connectable, schema=schema) |
1405 | 1359 |
|
1406 | 1360 | @contextmanager
|
1407 | 1361 | def run_transaction(self):
|
|
0 commit comments