-
-
Notifications
You must be signed in to change notification settings - Fork 301
Description
Describe the bug
A migration doesn't provide the name property of Enum if sqlalchemy.Enum is augmented with sqlalchemy.types.Decorator. Even if the name property is explicitly given.
Expected behavior
The property has to be provided in Enum's definition inside a migration.
To Reproduce
sql_types.py
from typing import Any
from sqlalchemy import Enum, TypeDecorator
class Enum1(Enum):
def __init__(self, *enums: object, **kw: Any):
validate_strings = kw.pop("validate_strings", True)
super().__init__(*enums, **kw, validate_strings=validate_strings)
class Enum2(TypeDecorator):
impl = Enum
cache_ok = True
def __init__(self, *enums: object, **kw: Any):
validate_strings = kw.pop("validate_strings", True)
super().__init__(*enums, **kw, validate_strings=validate_strings)tables.py
import enum
from sqlalchemy import Column, MetaData, Table
from sql_types import Enum1, Enum2
class TestEnum(str, enum.Enum):
FIRST = enum.auto()
SECOND = enum.auto()
metadata = MetaData()
test_table = Table(
"test",
metadata,
Column("col1", Enum1(TestEnum), nullable=False),
Column("col2", Enum2(TestEnum, name="enum2"), nullable=False),
)alembic revision --autogenerate -m "Enums" provides (shortened):
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('test',
sa.Column('col1', db.sql_types.Enum1('FIRST', 'SECOND', name='testenum'), nullable=False),
sa.Column('col2', db.sql_types.Enum2('FIRST', 'SECOND'), nullable=False)
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('test')
# ### end Alembic commands ###Error
As you can see from the migration enums are defined differently - the second one misses its name property.
If I try to run it on PostgreSQL I get the error:
Stacktrace
INFO [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO [alembic.runtime.migration] Generating static SQL
INFO [alembic.runtime.migration] Will assume transactional DDL.
BEGIN;
CREATE TABLE alembic_version (
version_num VARCHAR(32) NOT NULL,
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
);
INFO [alembic.runtime.migration] Running upgrade -> 264b6f57ae76, Enums
-- Running upgrade -> 264b6f57ae76
CREATE TYPE testenum AS ENUM ('FIRST', 'SECOND');
Traceback (most recent call last):
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\__main__.py", line 4, in <module>
main(prog="alembic")
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\config.py", line 636, in main
CommandLine(prog=prog).main(argv=argv)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\config.py", line 626, in main
self.run_cmd(cfg, options)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\config.py", line 603, in run_cmd
fn(
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\command.py", line 406, in upgrade
script.run_env()
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\script\base.py", line 586, in run_env
util.load_python_file(self.dir, "env.py")
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\util\pyfiles.py", line 95, in load_python_file
module = load_module_py(module_id, path)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\util\pyfiles.py", line 113, 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 "c:\Users\Михаил\PycharmProjects\Example Project\db\alembic\env.py", line 78, in <module>
run_migrations_offline()
File "c:\Users\Михаил\PycharmProjects\Example Project\db\alembic\env.py", line 52, in run_migrations_offline
context.run_migrations()
File "<string>", line 8, in run_migrations
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\runtime\environment.py", line 946, in run_migrations
self.get_context().run_migrations(**kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\runtime\migration.py", line 628, in run_migrations
step.migration_fn(**kw)
File "C:\Users\Михаил\PycharmProjects\Example Project\db\alembic\versions\264b6f57ae76_enums.py", line 24, in upgrade
op.create_table('test',
File "<string>", line 8, in create_table
File "<string>", line 3, in create_table
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\operations\ops.py", line 1318, in create_table
return operations.invoke(op)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\operations\base.py", line 442, in invoke
return fn(self, operation)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\operations\toimpl.py", line 143, in create_table
operations.impl.create_table(table, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\ddl\impl.py", line 366, in create_table
table.dispatch.before_create(
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\event\attr.py", line 497, in __call__
fn(*args, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\util\langhelpers.py", line 852, in __call__
return getattr(self.target, self.name)(*arg, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\sqltypes.py", line 1130, in _on_table_create
t._on_table_create(target, bind, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\dialects\postgresql\named_types.py", line 98, in _on_table_create
self.create(bind=bind, checkfirst=checkfirst)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\dialects\postgresql\named_types.py", line 338, in create
super().create(bind, checkfirst=checkfirst)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\dialects\postgresql\named_types.py", line 51, in create
bind._run_ddl_visitor(self.DDLGenerator, self, checkfirst=checkfirst)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\engine\mock.py", line 61, in _run_ddl_visitor
visitorcallable(self.dialect, self, **kwargs).traverse_single(element)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\visitors.py", line 664, in traverse_single
return meth(obj, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\dialects\postgresql\named_types.py", line 153, in visit_enum
self.connection.execute(CreateEnumType(enum))
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\engine\mock.py", line 69, in execute
return self._execute_impl(obj, parameters)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\runtime\migration.py", line 675, in dump
self.impl._exec(construct)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\alembic\ddl\impl.py", line 190, in _exec
compiled = construct.compile(dialect=self.dialect, **compile_kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\elements.py", line 308, in compile
return self._compiler(dialect, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\ddl.py", line 69, in _compiler
return dialect.ddl_compiler(dialect, self, **kw)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\compiler.py", line 870, in __init__
self.string = self.process(self.statement, **compile_kwargs)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\compiler.py", line 915, in process
return obj._compiler_dispatch(self, **kwargs)
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\sql\visitors.py", line 141, in _compiler_dispatch
return meth(self, **kw) # type: ignore # noqa: E501
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\dialects\postgresql\base.py", line 2236, in visit_create_enum_type
self.preparer.format_type(type_),
File "C:\Users\Михаил\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\sqlalchemy\dialects\postgresql\base.py", line 2728, in format_type
raise exc.CompileError(
sqlalchemy.exc.CompileError: PostgreSQL ENUM type requires a name.Versions.
- OS: Windows 11 23H2
- Python: 3.10.11
- Alembic: 1.13.3
- SQLAlchemy: 2.0.35
- Database: PostgreSQL 17.0.1
- DBAPI: psycopg2 2.9.9
Additional context
The docs state that subclassing from TypeDecorator should be preferred:
This method is preferred to direct subclassing of SQLAlchemy’s built-in types as it ensures that all required functionality of the underlying type is kept in place.
Have a nice day!