Skip to content

autodoc fails with AttributeError when importing SQLAlchemy models split across multiple files #14618

Description

@vivodi

Describe the bug

When using sphinx.ext.autodoc to document SQLAlchemy ORM models defined in separate files, Sphinx fails to import the second module and raises an AttributeError: type object 'ORM2' has no attribute '__table_args__'.

Note: If both ORM classes are placed in the same file, the documentation builds successfully.

Update: Setting autodoc_use_legacy_class_based = True solves the issue. This is a sub-issue of TRACKING: Problems in the new implementation of autodoc.

Running Sphinx v9.1.0
loading translations [en]... done
making output directory... done
loading intersphinx inventory 'sqlalchemy' from https://docs.sqlalchemy.org/objects.inv ...
intersphinx inventory has moved: https://docs.sqlalchemy.org/objects.inv -> https://docs.sqlalchemy.org/en/20/objects.inv
building [mo]: targets for 0 po files that are out of date
writing output... 
building [html]: targets for 1 source files that are out of date
updating environment: [new config] 1 added, 0 changed, 0 removed
reading sources... [100%] index
WARNING: autodoc: failed to import 'orm2' from module 'autodoc_sqlalchemy'; the following exception was raised:
Traceback (most recent call last):                                                                                                                                                  
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sphinx\ext\autodoc\_dynamic\_importer.py", line 221, in _import_module                            
    module = importlib.import_module(modname)                                                                                                                                       
  File "C:\Users\vivodi\AppData\Roaming\uv\python\cpython-3.14-windows-x86_64-none\Lib\importlib\__init__.py", line 88, in import_module                                             
    return _bootstrap._gcd_import(name[level:], package, level)                                                                                                                     
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                     
  File "<frozen importlib._bootstrap>", line 1398, in _gcd_import                                                                                                                   
  File "<frozen importlib._bootstrap>", line 1371, in _find_and_load                                                                                                                
  File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked                                                                                                       
  File "<frozen importlib._bootstrap>", line 938, in _load_unlocked                                                                                                                 
  File "<frozen importlib._bootstrap_external>", line 762, in exec_module                                                                                                           
  File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed                                                                                                      
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\autodoc_sqlalchemy\orm2.py", line 6, in <module>                                                                          
    class ORM2(Base):                                                                                                                                                               
        __tablename__ = "orm2"                                                                                                                                                      
        id: Mapped[int] = mapped_column(primary_key=True)                                                                                                                           
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sqlalchemy\orm\decl_api.py", line 849, in __init_subclass__                                       
    _as_declarative(cls._sa_registry, cls, cls.__dict__)                                                                                                                            
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                            
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sqlalchemy\orm\decl_base.py", line 245, in _as_declarative                                        
    return _MapperConfig.setup_mapping(registry, cls, dict_, None, {})                                                                                                              
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                              
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sqlalchemy\orm\decl_base.py", line 326, in setup_mapping                                          
    return _ClassScanMapperConfig(                                                                                                                                                  
        registry, cls_, dict_, table, mapper_kw                                                                                                                                     
    )                                                                                                                                                                               
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sqlalchemy\orm\decl_base.py", line 562, in __init__                                               
    self._scan_attributes()                                                                                                                                                         
    ~~~~~~~~~~~~~~~~~~~~~^^                                                                                                                                                         
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sqlalchemy\orm\decl_base.py", line 866, in _scan_attributes                                       
    table_args = cls_as_Decl.__table_args__                                                                                                                                         
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                                         
AttributeError: type object 'ORM2' has no attribute '__table_args__'. Did you mean: '__tablename__'?                                                                                
                                                                                                                                                                                    
The above exception was the direct cause of the following exception:                                                                                                                
                                                                                                                                                                                    
Traceback (most recent call last):                                                                                                                                                  
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sphinx\ext\autodoc\_dynamic\_importer.py", line 131, in _import_from_module_and_path              
    module = _import_module(module_name, try_reload=True)                                                                                                                           
  File "C:\Users\vivodi\Projects\autodoc_sqlalchemy\.venv\Lib\site-packages\sphinx\ext\autodoc\_dynamic\_importer.py", line 233, in _import_module                            
    raise ImportError(exc, traceback.format_exc()) from exc                                                                                                                         
ImportError: (AttributeError("type object 'ORM2' has no attribute '__table_args__'"), 'Traceback (most recent call last):\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\.venv\\Lib\\site-packages\\sphinx\\ext\\autodoc\\_dynamic\\_importer.py", line 221, in _import_module\n    module = importlib.import_module(modname)\n  File "C:\\Users\\vivodi\\AppData\\Roaming\\uv\\python\\cpython-3.14-windows-x86_64-none\\Lib\\importlib\\__init__.py", line 88, in import_module\n    return _bootstrap._gcd_import(name[level:], package, level)\n           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "<frozen importlib._bootstrap>", line 1398, in _gcd_import\n  File "<frozen importlib._bootstrap>", line 1371, in _find_and_load\n  File "<frozen importlib._bootstrap>", line 1342, in _find_and_load_unlocked\n  File "<frozen importlib._bootstrap>", line 938, in _load_unlocked\n  File "<frozen importlib._bootstrap_external>", line 762, in exec_module\n  File "<frozen importlib._bootstrap>", line 491, in _call_with_frames_removed\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\autodoc_sqlalchemy\\orm2.py", line 6, in <module>\n    class ORM2(Base):\n        __tablename__ = "orm2"\n        id: Mapped[int] = mapped_column(primary_key=True)\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\.venv\\Lib\\site-packages\\sqlalchemy\\orm\\decl_api.py", line 849, in __init_subclass__\n    _as_declarative(cls._sa_registry, cls, cls.__dict__)\n    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\.venv\\Lib\\site-packages\\sqlalchemy\\orm\\decl_base.py", line 245, in _as_declarative\n    return _MapperConfig.setup_mapping(registry, cls, dict_, None, {})\n           ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\.venv\\Lib\\site-packages\\sqlalchemy\\orm\\decl_base.py", line 326, in setup_mapping\n    return _ClassScanMapperConfig(\n        registry, cls_, dict_, table, mapper_kw\n    )\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\.venv\\Lib\\site-packages\\sqlalchemy\\orm\\decl_base.py", line 562, in __init__\n    self._scan_attributes()\n    ~~~~~~~~~~~~~~~~~~~~~^^\n  File "C:\\Users\\vivodi\\Projects\\autodoc_sqlalchemy\\.venv\\Lib\\site-packages\\sqlalchemy\\orm\\decl_base.py", line 866, in _scan_attributes\n    table_args = cls_as_Decl.__table_args__\n                 ^^^^^^^^^^^^^^^^^^^^^^^^^^\nAttributeError: type object \'ORM2\' has no attribute \'__table_args__\'. Did you mean: \'__tablename__\'?\n')                                                        
 [autodoc.import_object]                                                                                                                                                            
looking for now-outdated files... none found
pickling environment... done
checking consistency... done
preparing documents... done
copying assets... 
copying static files... 
Writing evaluated template result to C:\Users\vivodi\Projects\autodoc_sqlalchemy\docs\_build\html\_static\basic.css
Writing evaluated template result to C:\Users\vivodi\Projects\autodoc_sqlalchemy\docs\_build\html\_static\documentation_options.js
Writing evaluated template result to C:\Users\vivodi\Projects\autodoc_sqlalchemy\docs\_build\html\_static\language_data.js
Writing evaluated template result to C:\Users\vivodi\Projects\autodoc_sqlalchemy\docs\_build\html\_static\alabaster.css
copying static files: done
copying extra files... 
copying extra files: done
copying assets: done
writing output... [100%] index
generating indices... genindex py-modindex done
writing additional pages... search done
dumping search index in English (code: en)... done
dumping object inventory... done
build succeeded, 1 warning.

The HTML pages are in _build\html.

How to Reproduce

Project Structure:

autodoc_sqlalchemy/
├── __init__.py
├── orm1.py
└── orm2.py

autodoc_sqlalchemy/__init__.py

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

autodoc_sqlalchemy/orm1.py

from sqlalchemy.orm import Mapped, mapped_column
from autodoc_sqlalchemy import Base

class ORM1(Base):
    __tablename__ = "orm1"
    id: Mapped[int] = mapped_column(primary_key=True)

autodoc_sqlalchemy/orm2.py

from sqlalchemy.orm import Mapped, mapped_column
from autodoc_sqlalchemy.orm1 import Base

class ORM2(Base):
    __tablename__ = "orm2"
    id: Mapped[int] = mapped_column(primary_key=True)

docs/index.rst

.. automodule:: autodoc_sqlalchemy.orm1
   :members:
   :private-members:
   :show-inheritance:
   :undoc-members:

.. automodule:: autodoc_sqlalchemy.orm2
   :members:
   :private-members:
   :show-inheritance:
   :undoc-members:

git clone method to reproduce:

git clone https://github.com/vivodi/autodoc-sqlalchemy-demo.git
cd autodoc-sqlalchemy-demo
uv sync
uv run sphinx-build -M html docs docs/_build

Environment Information

Platform:              win32; (Windows-11-10.0.26200-SP0)
Python version:        3.14.0 (main, Nov 19 2025, 22:43:52) [MSC v.1944 64 bit (AMD64)])
Python implementation: CPython
Sphinx version:        9.1.0
Docutils version:      0.22.4
Jinja2 version:        3.1.6
Pygments version:      2.21.0

Sphinx extensions

["sphinx.ext.autodoc", "sphinx.ext.intersphinx"]

Additional context

sqlalchemy version: 2.0.52

I also reported this issue to SQLAlchemy, but it remains unresolved. sqlalchemy/sqlalchemy#13527

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions