Search code examples
pythonsqlalchemyalembicgoogle-cloud-spanner

Unable to set cloud spanner dialect for Alembic


I have installed the following packages -

SQLAlchemy==1.4.41
sqlalchemy-spanner==1.2.2
alembic==1.8.1

Then I created a file main.py where I am creating a SQLAlchemy engine for cloud spanner.

from sqlalchemy import create_engine, MetaData
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine(
    "spanner+spanner:///projects/spanner-project/instances/spanner-instance/databases/spanner-database"
)
metadata = MetaData()
Base = declarative_base(metadata=metadata)

Then in alembic.ini I have used the same url as shown above for engine.

sqlalchemy.url = spanner+spanner:///projects/spanner-project/instances/spanner-instance/databases/spanner-database

Also in env.py I have set the base's matadata as shown below -

from app.main import Base
target_metadata = Base.metadata

I also have a models.py which has just one model as shown below -

from app.main import Base
from sqlalchemy import (
    Column,
    Integer,
    String,
)


class Users(Base):
    __tablename__ = "users"
    __table_args__ = {
        'extend_existing': True,
    }
    id = Column(Integer, primary_key=True)
    name = Column(String(50))

When I run the alembic command to generate migrations I see an exception of KeyError: 'spanner+spanner'.

CMD -

alembic revision --autogenerate -m "first migrations"

Exception -

Traceback (most recent call last):
  File "/usr/local/bin/alembic", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.10/site-packages/alembic/config.py", line 590, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/usr/local/lib/python3.10/site-packages/alembic/config.py", line 584, in main
    self.run_cmd(cfg, options)
  File "/usr/local/lib/python3.10/site-packages/alembic/config.py", line 561, in run_cmd
    fn(
  File "/usr/local/lib/python3.10/site-packages/alembic/command.py", line 229, in revision
    script_directory.run_env()
  File "/usr/local/lib/python3.10/site-packages/alembic/script/base.py", line 569, in run_env
    util.load_python_file(self.dir, "env.py")
  File "/usr/local/lib/python3.10/site-packages/alembic/util/pyfiles.py", line 94, in load_python_file
    module = load_module_py(module_id, path)
  File "/usr/local/lib/python3.10/site-packages/alembic/util/pyfiles.py", line 110, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/code/app/alembic/env.py", line 79, in <module>
    run_migrations_online()
  File "/code/app/alembic/env.py", line 68, in run_migrations_online
    context.configure(
  File "<string>", line 8, in configure
  File "/usr/local/lib/python3.10/site-packages/alembic/runtime/environment.py", line 822, in configure
    self._migration_context = MigrationContext.configure(
  File "/usr/local/lib/python3.10/site-packages/alembic/runtime/migration.py", line 268, in configure
    return MigrationContext(dialect, connection, opts, environment_context)
  File "/usr/local/lib/python3.10/site-packages/alembic/runtime/migration.py", line 196, in __init__
    self.impl = ddl.DefaultImpl.get_by_dialect(dialect)(
  File "/usr/local/lib/python3.10/site-packages/alembic/ddl/impl.py", line 123, in get_by_dialect
    return _impls[dialect.name]

I tried to by changing the URL in alembic.ini from spanner+sapnner:///..... to spanner:///... but then I got this exception -

Traceback (most recent call last):
  File "/usr/local/bin/alembic", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.10/site-packages/alembic/config.py", line 590, in main
    CommandLine(prog=prog).main(argv=argv)
  File "/usr/local/lib/python3.10/site-packages/alembic/config.py", line 584, in main
    self.run_cmd(cfg, options)
  File "/usr/local/lib/python3.10/site-packages/alembic/config.py", line 561, in run_cmd
    fn(
  File "/usr/local/lib/python3.10/site-packages/alembic/command.py", line 229, in revision
    script_directory.run_env()
  File "/usr/local/lib/python3.10/site-packages/alembic/script/base.py", line 569, in run_env
    util.load_python_file(self.dir, "env.py")
  File "/usr/local/lib/python3.10/site-packages/alembic/util/pyfiles.py", line 94, in load_python_file
    module = load_module_py(module_id, path)
  File "/usr/local/lib/python3.10/site-packages/alembic/util/pyfiles.py", line 110, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/code/app/alembic/env.py", line 79, in <module>
    run_migrations_online()
  File "/code/app/alembic/env.py", line 61, in run_migrations_online
    connectable = engine_from_config(
  File "/usr/local/lib/python3.10/site-packages/sqlalchemy/engine/create.py", line 743, in engine_from_config
    return create_engine(url, **options)
  File "<string>", line 2, in create_engine
  File "/usr/local/lib/python3.10/site-packages/sqlalchemy/util/deprecations.py", line 309, in warned
    return fn(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/sqlalchemy/engine/create.py", line 522, in create_engine
    entrypoint = u._get_entrypoint()
  File "/usr/local/lib/python3.10/site-packages/sqlalchemy/engine/url.py", line 655, in _get_entrypoint
    cls = registry.load(name)
  File "/usr/local/lib/python3.10/site-packages/sqlalchemy/util/langhelpers.py", line 343, in load
    raise exc.NoSuchModuleError(
sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:spanner

Also when I use spanner+spanner:///... to creating and engine and then further use it to create db tables then I am able to do that successfully.

from app.main import engine, Base
from sqlalchemy import (
    Column,
    Integer,
    String,
)


class Users(Base):
    __tablename__ = "users"
    __table_args__ = {
        'extend_existing': True,
    }
    id = Column(Integer, primary_key=True)
    name = Column(String(50))


Base.metadata.create_all(engine)

Just by running this model I am able to create the table.

python models.py

What do I do to use alembic with spanner?


Solution

  • You're using SQLAlchemy 1.4, which means it must be everywhere spanner+spanner. Just spanner will not work. As the problem appears only for Alembic, reinstall will also not help - the problem is in configurations somewhere.

    Can you show your env.py file? There must be written something like this:

    class SpannerImpl(DefaultImpl):
        __dialect__ = "spanner+spanner"
    

    Looking at your traceback, I suspect, in the env.py the dialect is written as just spanner, which causes the error.