diff options
Diffstat (limited to 'lib/sqlalchemy/engine/reflection.py')
-rw-r--r-- | lib/sqlalchemy/engine/reflection.py | 233 |
1 files changed, 160 insertions, 73 deletions
diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py index d113588bb..25538fddb 100644 --- a/lib/sqlalchemy/engine/reflection.py +++ b/lib/sqlalchemy/engine/reflection.py @@ -25,7 +25,11 @@ methods such as get_table_names, get_columns, etc. 'name' attribute.. """ +import contextlib + from .base import Connectable +from .base import Connection +from .base import Engine from .. import exc from .. import inspection from .. import sql @@ -64,24 +68,27 @@ class Inspector(object): fetched metadata. A :class:`.Inspector` object is usually created via the - :func:`.inspect` function:: + :func:`.inspect` function, which may be passed an :class:`.Engine` + or a :class:`.Connection`:: from sqlalchemy import inspect, create_engine engine = create_engine('...') insp = inspect(engine) - The inspection method above is equivalent to using the - :meth:`.Inspector.from_engine` method, i.e.:: - - engine = create_engine('...') - insp = Inspector.from_engine(engine) - - Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` may opt - to return an :class:`.Inspector` subclass that provides additional - methods specific to the dialect's target database. + Where above, the :class:`~sqlalchemy.engine.interfaces.Dialect` associated + with the engine may opt to return an :class:`.Inspector` subclass that + provides additional methods specific to the dialect's target database. """ + @util.deprecated( + "1.4", + "The __init__() method on :class:`.Inspector` is deprecated and " + "will be removed in a future release. Please use the " + ":func:`.sqlalchemy.inspect` " + "function on an :class:`.Engine` or :class:`.Connection` in order to " + "acquire an :class:`.Inspector`.", + ) def __init__(self, bind): """Initialize a new :class:`.Inspector`. @@ -94,23 +101,47 @@ class Inspector(object): :meth:`.Inspector.from_engine` """ - # this might not be a connection, it could be an engine. - self.bind = bind + return self._init_legacy(bind) + + @classmethod + def _construct(cls, init, bind): - # set the engine + if hasattr(bind.dialect, "inspector"): + cls = bind.dialect.inspector + + self = cls.__new__(cls) + init(self, bind) + return self + + def _init_legacy(self, bind): if hasattr(bind, "engine"): - self.engine = bind.engine + self._init_connection(bind) else: - self.engine = bind + self._init_engine(bind) - if self.engine is bind: - # if engine, ensure initialized - bind.connect().close() + def _init_engine(self, engine): + self.bind = self.engine = engine + engine.connect().close() + self._op_context_requires_connect = True + self.dialect = self.engine.dialect + self.info_cache = {} + def _init_connection(self, connection): + self.bind = connection + self.engine = connection.engine + self._op_context_requires_connect = False self.dialect = self.engine.dialect self.info_cache = {} @classmethod + @util.deprecated( + "1.4", + "The from_engine() method on :class:`.Inspector` is deprecated and " + "will be removed in a future release. Please use the " + ":func:`.sqlalchemy.inspect` " + "function on an :class:`.Engine` or :class:`.Connection` in order to " + "acquire an :class:`.Inspector`.", + ) def from_engine(cls, bind): """Construct a new dialect-specific Inspector object from the given engine or connection. @@ -129,13 +160,53 @@ class Inspector(object): See the example at :class:`.Inspector`. """ - if hasattr(bind.dialect, "inspector"): - return bind.dialect.inspector(bind) - return Inspector(bind) + return cls._construct(cls._init_legacy, bind) @inspection._inspects(Connectable) - def _insp(bind): - return Inspector.from_engine(bind) + def _connectable_insp(bind): + # this method should not be used unless some unusual case + # has subclassed "Connectable" + + return Inspector._construct(Inspector._init_legacy, bind) + + @inspection._inspects(Engine) + def _engine_insp(bind): + return Inspector._construct(Inspector._init_engine, bind) + + @inspection._inspects(Connection) + def _connection_insp(bind): + return Inspector._construct(Inspector._init_connection, bind) + + @contextlib.contextmanager + def _operation_context(self): + """Return a context that optimizes for multiple operations on a single + transaction. + + This essentially allows connect()/close() to be called if we detected + that we're against an :class:`.Engine` and not a :class:`.Connection`. + + """ + if self._op_context_requires_connect: + conn = self.bind.connect() + else: + conn = self.bind + try: + yield conn + finally: + if self._op_context_requires_connect: + conn.close() + + @contextlib.contextmanager + def _inspection_context(self): + """Return an :class:`.Inspector` from this one that will run all + operations on a single connection. + + """ + + with self._operation_context() as conn: + sub_insp = self._construct(self.__class__._init_connection, conn) + sub_insp.info_cache = self.info_cache + yield sub_insp @property def default_schema_name(self): @@ -153,9 +224,10 @@ class Inspector(object): """ if hasattr(self.dialect, "get_schema_names"): - return self.dialect.get_schema_names( - self.bind, info_cache=self.info_cache - ) + with self._operation_context() as conn: + return self.dialect.get_schema_names( + conn, info_cache=self.info_cache + ) return [] def get_table_names(self, schema=None): @@ -185,9 +257,10 @@ class Inspector(object): """ - return self.dialect.get_table_names( - self.bind, schema, info_cache=self.info_cache - ) + with self._operation_context() as conn: + return self.dialect.get_table_names( + conn, schema, info_cache=self.info_cache + ) def has_table(self, table_name, schema=None): """Return True if the backend has a table of the given name. @@ -196,7 +269,8 @@ class Inspector(object): """ # TODO: info_cache? - return self.dialect.has_table(self.bind, table_name, schema) + with self._operation_context() as conn: + return self.dialect.has_table(conn, table_name, schema) def get_sorted_table_and_fkc_names(self, schema=None): """Return dependency-sorted table and foreign key constraint names in @@ -222,12 +296,11 @@ class Inspector(object): with an already-given :class:`.MetaData`. """ - if hasattr(self.dialect, "get_table_names"): + + with self._operation_context() as conn: tnames = self.dialect.get_table_names( - self.bind, schema, info_cache=self.info_cache + conn, schema, info_cache=self.info_cache ) - else: - tnames = self.engine.table_names(schema) tuples = set() remaining_fkcs = set() @@ -263,9 +336,11 @@ class Inspector(object): .. versionadded:: 1.0.0 """ - return self.dialect.get_temp_table_names( - self.bind, info_cache=self.info_cache - ) + + with self._operation_context() as conn: + return self.dialect.get_temp_table_names( + conn, info_cache=self.info_cache + ) def get_temp_view_names(self): """return a list of temporary view names for the current bind. @@ -276,9 +351,10 @@ class Inspector(object): .. versionadded:: 1.0.0 """ - return self.dialect.get_temp_view_names( - self.bind, info_cache=self.info_cache - ) + with self._operation_context() as conn: + return self.dialect.get_temp_view_names( + conn, info_cache=self.info_cache + ) def get_table_options(self, table_name, schema=None, **kw): """Return a dictionary of options specified when the table of the @@ -295,9 +371,10 @@ class Inspector(object): """ if hasattr(self.dialect, "get_table_options"): - return self.dialect.get_table_options( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_table_options( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) return {} def get_view_names(self, schema=None): @@ -308,9 +385,10 @@ class Inspector(object): """ - return self.dialect.get_view_names( - self.bind, schema, info_cache=self.info_cache - ) + with self._operation_context() as conn: + return self.dialect.get_view_names( + conn, schema, info_cache=self.info_cache + ) def get_view_definition(self, view_name, schema=None): """Return definition for `view_name`. @@ -320,9 +398,10 @@ class Inspector(object): """ - return self.dialect.get_view_definition( - self.bind, view_name, schema, info_cache=self.info_cache - ) + with self._operation_context() as conn: + return self.dialect.get_view_definition( + conn, view_name, schema, info_cache=self.info_cache + ) def get_columns(self, table_name, schema=None, **kw): """Return information about columns in `table_name`. @@ -354,9 +433,10 @@ class Inspector(object): """ - col_defs = self.dialect.get_columns( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + col_defs = self.dialect.get_columns( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) for col_def in col_defs: # make this easy and only return instances for coltype coltype = col_def["type"] @@ -377,9 +457,10 @@ class Inspector(object): primary key information as a list of column names. """ - return self.dialect.get_pk_constraint( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - )["constrained_columns"] + with self._operation_context() as conn: + return self.dialect.get_pk_constraint( + conn, table_name, schema, info_cache=self.info_cache, **kw + )["constrained_columns"] def get_pk_constraint(self, table_name, schema=None, **kw): """Return information about primary key constraint on `table_name`. @@ -401,9 +482,10 @@ class Inspector(object): use :class:`.quoted_name`. """ - return self.dialect.get_pk_constraint( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_pk_constraint( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) def get_foreign_keys(self, table_name, schema=None, **kw): """Return information about foreign_keys in `table_name`. @@ -436,9 +518,10 @@ class Inspector(object): """ - return self.dialect.get_foreign_keys( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_foreign_keys( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) def get_indexes(self, table_name, schema=None, **kw): """Return information about indexes in `table_name`. @@ -476,9 +559,10 @@ class Inspector(object): """ - return self.dialect.get_indexes( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_indexes( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) def get_unique_constraints(self, table_name, schema=None, **kw): """Return information about unique constraints in `table_name`. @@ -501,9 +585,10 @@ class Inspector(object): """ - return self.dialect.get_unique_constraints( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_unique_constraints( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) def get_table_comment(self, table_name, schema=None, **kw): """Return information about the table comment for ``table_name``. @@ -521,9 +606,10 @@ class Inspector(object): """ - return self.dialect.get_table_comment( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_table_comment( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) def get_check_constraints(self, table_name, schema=None, **kw): """Return information about check constraints in `table_name`. @@ -554,9 +640,10 @@ class Inspector(object): """ - return self.dialect.get_check_constraints( - self.bind, table_name, schema, info_cache=self.info_cache, **kw - ) + with self._operation_context() as conn: + return self.dialect.get_check_constraints( + conn, table_name, schema, info_cache=self.info_cache, **kw + ) def reflecttable( self, |