Skip to content

Commit 72dcc3e

Browse files
Fix compatibility with sqlalchemy>=2 (#117)
1 parent e50a768 commit 72dcc3e

File tree

6 files changed

+23
-28
lines changed

6 files changed

+23
-28
lines changed

omniduct/databases/_schemas.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class SchemasMixin:
6161
6262
It is currently implemented as a mixin rather than directly provided on the
6363
base class because it requires that the host `DatabaseClient` instance have a
64-
`sqlalchemy` metadata object handle, and not all backends support this.
64+
`sqlalchemy` engine object handle, and not all backends support this.
6565
6666
If we are willing to forgo the ability to actually make queries using the
6767
SQLAlchemy ORM, we could instead use an SQL agnostic version.
@@ -79,9 +79,9 @@ def schemas(self):
7979
def get_schemas():
8080
if not getattr(self, "_schemas", None):
8181
assert (
82-
getattr(self, "_sqlalchemy_metadata", None) is not None
83-
), f"`{self.__class__.__name__}` instances do not provide the required sqlalchemy metadata for schema exploration."
84-
self._schemas = Schemas(self._sqlalchemy_metadata)
82+
getattr(self, "_sqlalchemy_engine", None) is not None
83+
), f"`{self.__class__.__name__}` instances do not provide the required sqlalchemy engine for schema exploration."
84+
self._schemas = Schemas(self._sqlalchemy_engine)
8585
return self._schemas
8686

8787
return Proxy(get_schemas)
@@ -138,22 +138,20 @@ class Schemas:
138138
An object which has as its attributes all of the schemas in a nominated database.
139139
140140
Args:
141-
metadata (sqlalchemy.MetaData): A SQL Alchemy `MetaData` instance
141+
engine (sqlalchemy.Engine): A SQL Alchemy `Engine` instance
142142
configured for the nominated database.
143143
"""
144144

145-
def __init__(self, metadata):
146-
self._metadata = metadata
145+
def __init__(self, engine):
146+
self._engine = engine
147147
self._schema_names = None
148148
self._schema_cache = {}
149149

150150
@property
151151
def all(self):
152152
"list<str>: The list of schema names."
153153
if self._schema_names is None:
154-
self._schema_names = sqlalchemy.inspect(
155-
self._metadata.bind
156-
).get_schema_names()
154+
self._schema_names = sqlalchemy.inspect(self._engine).get_schema_names()
157155
return self._schema_names
158156

159157
def __dir__(self):
@@ -162,9 +160,7 @@ def __dir__(self):
162160
def __getattr__(self, value):
163161
if value in self.all:
164162
if value not in self._schema_cache:
165-
self._schema_cache[value] = Schema(
166-
metadata=self._metadata, schema=value
167-
)
163+
self._schema_cache[value] = Schema(engine=self._engine, schema=value)
168164
return self._schema_cache[value]
169165
raise AttributeError(f"No such schema {value}")
170166

@@ -184,13 +180,13 @@ class Schema:
184180
An object which has as its attributes all of the tables in a nominated database schema.
185181
186182
Args:
187-
metadata (sqlalchemy.MetaData): A SQL Alchemy `MetaData` instance
183+
engine (sqlalchemy.Engine): A SQL Alchemy `Engine` instance
188184
configured for the nominated database.
189185
schema (str): The schema within which to expose tables.
190186
"""
191187

192-
def __init__(self, metadata, schema):
193-
self._metadata = metadata
188+
def __init__(self, engine, schema):
189+
self._engine = engine
194190
self._schema = schema
195191
self._table_cache = {}
196192
self._table_names = None
@@ -199,7 +195,7 @@ def __init__(self, metadata, schema):
199195
def all(self):
200196
"""list<str>: The table names in this database schema."""
201197
if self._table_names is None:
202-
self._table_names = sqlalchemy.inspect(self._metadata.bind).get_table_names(
198+
self._table_names = sqlalchemy.inspect(self._engine).get_table_names(
203199
self._schema
204200
)
205201
return self._table_names

omniduct/databases/base.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ def __init__(
122122
self._templates = templates or {}
123123
self._template_context = template_context or {}
124124
self._sqlalchemy_engine = None
125-
self._sqlalchemy_metadata = None
126125
self._default_format_opts = default_format_opts or {}
127126

128127
self._init(**kwargs)

omniduct/databases/hiveserver2.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def _init(
117117

118118
@override
119119
def _connect(self):
120-
from sqlalchemy import create_engine, MetaData
120+
from sqlalchemy import create_engine
121121

122122
if self.driver == "pyhive":
123123
try:
@@ -142,7 +142,6 @@ def _connect(self):
142142
self._sqlalchemy_engine = create_engine(
143143
f"hive://{self.host}:{self.port}/{self.schema}"
144144
)
145-
self._sqlalchemy_metadata = MetaData(self._sqlalchemy_engine)
146145
elif self.driver == "impyla":
147146
try:
148147
import impala.dbapi
@@ -165,7 +164,6 @@ def _connect(self):
165164
self._sqlalchemy_engine = create_engine(
166165
f"impala://{self.host}:{self.port}/{self.schema}"
167166
)
168-
self._sqlalchemy_metadata = MetaData(self._sqlalchemy_engine)
169167

170168
def __hive_cursor(self):
171169
if (
@@ -192,7 +190,6 @@ def _disconnect(self):
192190
# pylint: disable-next=attribute-defined-outside-init
193191
self.__hive = None
194192
self._sqlalchemy_engine = None
195-
self._sqlalchemy_metadata = None
196193
# pylint: disable-next=attribute-defined-outside-init
197194
self._schemas = None
198195

omniduct/databases/presto.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,13 @@ def source(self, source):
9090

9191
@override
9292
def _connect(self):
93-
from sqlalchemy import create_engine, MetaData
93+
from sqlalchemy import create_engine
9494

9595
logging.getLogger("pyhive").setLevel(1000) # Silence pyhive logging.
9696
logger.info("Connecting to Presto coordinator...")
9797
self._sqlalchemy_engine = create_engine(
9898
f"presto://{self.host}:{self.port}/{self.catalog}/{self.schema}"
9999
)
100-
self._sqlalchemy_metadata = MetaData(self._sqlalchemy_engine)
101100

102101
@override
103102
def _is_connected(self):
@@ -107,7 +106,6 @@ def _is_connected(self):
107106
def _disconnect(self):
108107
logger.info("Disconnecting from Presto coordinator...")
109108
self._sqlalchemy_engine = None
110-
self._sqlalchemy_metadata = None
111109
self._schemas = None # pylint: disable=attribute-defined-outside-init
112110

113111
# Querying

omniduct/databases/sqlalchemy.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ def db_uri(self):
7070
database=self.database,
7171
)
7272

73+
@property
74+
def _sqlalchemy_engine(self):
75+
"""
76+
The SQLAlchemy engine object for the SchemasMixin.
77+
"""
78+
return self.engine
79+
7380
@override
7481
def _connect(self):
7582
import sqlalchemy
@@ -85,7 +92,6 @@ def _connect(self):
8592

8693
# pylint: disable-next=attribute-defined-outside-init
8794
self.engine = sqlalchemy.create_engine(self.db_uri, **self.engine_opts)
88-
self._sqlalchemy_metadata = sqlalchemy.MetaData(self.engine)
8995

9096
@override
9197
def _is_connected(self):
@@ -95,7 +101,6 @@ def _is_connected(self):
95101
def _disconnect(self):
96102
# pylint: disable-next=attribute-defined-outside-init
97103
self.engine = None
98-
self._sqlalchemy_metadata = None
99104
# pylint: disable-next=attribute-defined-outside-init
100105
self._schemas = None
101106

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ name = "omniduct"
77
dynamic = ["version"]
88
description = "A toolkit providing a uniform interface for connecting to and extracting data from a wide variety of (potentially remote) data stores (including HDFS, Hive, Presto, MySQL, etc)."
99
readme = "README.md"
10-
license = ""
10+
license = "MIT"
1111
authors = [
1212
{ name = "Matthew Wardrop", email = "[email protected]" },
1313
{ name = "Dan Frank", email = "[email protected]" },

0 commit comments

Comments
 (0)