kvesteri / sqlalchemy-continuum

Versioning extension for SQLAlchemy.
BSD 3-Clause "New" or "Revised" License
568 stars 128 forks source link

AttributeError: '_ProxyImpl' object has no attribute 'dispatch' at configure_mappers step (sqlalchemy v2.0) #336

Closed amnan98 closed 9 months ago

amnan98 commented 9 months ago

My issue is similar to #9458 except with DescriptorProperty I have some versioned tables in my project using SQLAlchemy-Continuum (v1.4.0) and for migrations I use Alembic (v1.12.0) and I'm upgrading SQLAlchemy from v1.4.49 to v2.0.21 (no deprecation warnings). now when I try to run migrations or create a db object I similar errors.

when creating the object:

venv/lib/python3.11/site-packages/sqlalchemy/orm/state.py:561: in _initialize_instance
    manager.dispatch.init(self, args, kwargs)
venv/lib/python3.11/site-packages/sqlalchemy/event/attr.py:487: in __call__
    fn(*args, **kw)
venv/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py:4391: in _event_on_init
    instrumenting_mapper._check_configure()
venv/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py:2386: in _check_configure
    _configure_registries({self.registry}, cascade=True)
venv/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py:4202: in _configure_registries
    Mapper.dispatch._for_class(Mapper).after_configured()  # type: ignore
venv/lib/python3.11/site-packages/sqlalchemy/event/attr.py:378: in __call__
    fn(*args, **kw)
venv/lib/python3.11/site-packages/sqlalchemy/orm/events.py:887: in wrap
    fn(*arg, **kw)
venv/lib/python3.11/site-packages/sqlalchemy_continuum/builder.py:23: in check_reentry
    handler(*args, **kwargs)
venv/lib/python3.11/site-packages/sqlalchemy_continuum/builder.py:191: in configure_versioned_classes
    self.enable_active_history(pending_classes_copies)
venv/lib/python3.11/site-packages/sqlalchemy_continuum/builder.py:205: in enable_active_history
    impl.active_history = True
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _  

self = <sqlalchemy.orm.descriptor_props.DescriptorProperty.instrument_class.<locals>._ProxyImpl object at 0x7f22e9b5ecb0>, value = True

    def _set_active_history(self, value):
>       self.dispatch._active_history = value
E       AttributeError: '_ProxyImpl' object has no attribute 'dispatch'

when running migrations:

Traceback (most recent call last):
  File "~/venv/bin/alembic", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "~/venv/lib/python3.11/site-packages/alembic/config.py", line 630, in main
    CommandLine(prog=prog).main(argv=argv)
  File "~/venv/lib/python3.11/site-packages/alembic/config.py", line 624, in main
    self.run_cmd(cfg, options)
  File "~/venv/lib/python3.11/site-packages/alembic/config.py", line 601, in run_cmd
    fn(
  File "~/venv/lib/python3.11/site-packages/alembic/command.py", line 399, in upgrade
    script.run_env()
  File "~/venv/lib/python3.11/site-packages/alembic/script/base.py", line 578, in run_env
    util.load_python_file(self.dir, "env.py")
  File "~/venv/lib/python3.11/site-packages/alembic/util/pyfiles.py", line 93, in load_python_file
    module = load_module_py(module_id, path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/venv/lib/python3.11/site-packages/alembic/util/pyfiles.py", line 109, in load_module_py
    spec.loader.exec_module(module)  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "~/app/alembic/env.py", line 60, in <module>
    configure_mappers()
  File "~/venv/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py", line 4168, in configure_mappers
    _configure_registries(_all_registries(), cascade=True)
  File "~/venv/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py", line 4202, in _configure_registries
    Mapper.dispatch._for_class(Mapper).after_configured()  # type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/venv/lib/python3.11/site-packages/sqlalchemy/event/attr.py", line 378, in __call__
    fn(*args, **kw)
  File "~/venv/lib/python3.11/site-packages/sqlalchemy/orm/events.py", line 887, in wrap
    fn(*arg, **kw)
  File "~/venv/lib/python3.11/site-packages/sqlalchemy_continuum/builder.py", line 23, in check_reentry
    handler(*args, **kwargs)
  File "~/venv/lib/python3.11/site-packages/sqlalchemy_continuum/builder.py", line 191, in configure_versioned_classes
    self.enable_active_history(pending_classes_copies)
  File "~/venv/lib/python3.11/site-packages/sqlalchemy_continuum/builder.py", line 205, in enable_active_history
    impl.active_history = True
    ^^^^^^^^^^^^^^^^^^^
  File "~/venv/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 942, in _set_active_history
    self.dispatch._active_history = value
    ^^^^^^^^^^^^^
AttributeError: '_ProxyImpl' object has no attribute 'dispatch'

Reason why I opened the issue here instead of sqlalchemy is because when I add if isinstance(prop, ConcreteInheritedProperty) or isinstance(prop, DescriptorProperty): to builder.py L#200 everything starts working again.

Here's a minimal working example:

import uuid
from datetime import datetime, timezone
from sqlalchemy import String, DateTime
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import (
    DeclarativeBase,
    Mapped,
    mapped_column,
    configure_mappers,
    declared_attr,
    synonym,
)
from sqlalchemy_continuum import make_versioned

make_versioned(user_cls=None)

class Base(DeclarativeBase):
    pass

class TableBase:
    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, index=True, default=uuid.uuid4
    )
    create_time: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, default=datetime.now(timezone.utc)
    )

    @declared_attr
    def createTime(cls):
        return synonym("create_time")

class A(Base, TableBase):
    __versioned__ = {}
    __tablename__ = "table_a"

    property_a: Mapped[str] = mapped_column(String)

configure_mappers()