def make_mock_engine(name): """ Creates a mock sqlalchemy engine for testing dialect functionality """ if Version(sa.__version__) >= Version('1.4.0'): return sa.create_mock_engine(URL(drivername=name), executor=None) else: return sa.create_engine(URL(drivername=name, ), strategy='mock', executor=None)
def print_schema(metadata=_globals.REGISTRY.metadata, *, file=None, engine=_globals.ENGINE): """Print the SQL from metadata.create_all() without executing.""" def print_sql(sql): print(sql.compile(dialect=engine.dialect), file=file) mock_engine = sa.create_mock_engine(engine.url, executor=print_sql) metadata.create_all(mock_engine, checkfirst=False)
def mock_engine(self): def executor(*a, **kw): return None engine = create_mock_engine(testing.db.name + "://", executor) # fmt: off engine.dialect.identifier_preparer = \ tsa.sql.compiler.IdentifierPreparer( engine.dialect ) # fmt: on return engine
def get_ddl(*cbs: Callable[[sqlalchemy.engine.Engine], None]) -> str: # By default sqlalchemy treats asyncpg as if it had paramstyle="format", which means it tries to escape percent # signs. We don't want that so we have to override the paramstyle. Ideally "numeric" would be the right choice here # but that doesn't work. dialect = sqlalchemy.dialects.postgresql.dialect( paramstyle="qmark") # type: ignore ddls = [] def executor(sql: sqlalchemy.schema.DDLElement) -> None: ddls.append(str(sql.compile(dialect=dialect)) + ";") mock_engine = sqlalchemy.create_mock_engine("postgresql://", executor) for cb in cbs: cb(mock_engine) # type: ignore return "\n".join(ddls)
def dump_model_ddl(dialect: str, metadata: str): """ :param dialect: :param metadata: """ dialect = dialect or "sqlite" _metadata = import_from_module(metadata) engine = create_mock_engine( f"{dialect}://", executor=lambda sql, *_, **__: print( str(sql.compile(dialect=engine.dialect)).replace("\n\n", ";" ) # type: ignore ), ) _metadata.create_all(engine)
def create_mock_engine(bind, stream=None): """Create a mock SQLAlchemy engine from the passed engine or bind URL. :param bind: A SQLAlchemy engine or bind URL to mock. :param stream: Render all DDL operations to the stream. """ if not isinstance(bind, six.string_types): bind_url = str(bind.url) else: bind_url = bind if stream is not None: def dump(sql, *args, **kwargs): class Compiler(type(sql._compiler(engine.dialect))): def visit_bindparam(self, bindparam, *args, **kwargs): return self.render_literal_value(bindparam.value, bindparam.type) def render_literal_value(self, value, type_): if isinstance(value, six.integer_types): return str(value) elif isinstance(value, (datetime.date, datetime.datetime)): return "'%s'" % value return super(Compiler, self).render_literal_value(value, type_) text = str(Compiler(engine.dialect, sql).process(sql)) text = re.sub(r'\n+', '\n', text) text = text.strip('\n').strip() stream.write('\n%s;' % text) else: def dump(*args, **kw): return None try: engine = sa.create_mock_engine(bind_url, executor=dump) except AttributeError: # SQLAlchemy <1.4 engine = sa.create_engine(bind_url, strategy='mock', executor=dump) return engine
def mock_engine(dialect_name=None): """Provides a mocking engine based on the current testing.db. This is normally used to test DDL generation flow as emitted by an Engine. It should not be used in other cases, as assert_compile() and assert_sql_execution() are much better choices with fewer moving parts. """ from sqlalchemy import create_mock_engine if not dialect_name: dialect_name = config.db.name buffer = [] def executor(sql, *a, **kw): buffer.append(sql) def assert_sql(stmts): recv = [re.sub(r"[\n\t]", "", str(s)) for s in buffer] assert recv == stmts, recv def print_sql(): d = engine.dialect return "\n".join(str(s.compile(dialect=d)) for s in engine.mock) engine = create_mock_engine(dialect_name + "://", executor) assert not hasattr(engine, "mock") engine.mock = buffer engine.assert_sql = assert_sql engine.print_sql = print_sql return engine