Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KeyError: 'created_by' when building documentation #128

Closed
PipeKnight opened this issue Jun 5, 2022 · 7 comments
Closed

KeyError: 'created_by' when building documentation #128

PipeKnight opened this issue Jun 5, 2022 · 7 comments
Assignees
Labels
bug Something isn't working

Comments

@PipeKnight
Copy link

Full error trace:

# Sphinx version: 5.0.0
# Python version: 3.8.13 (CPython)
# Docutils version: 0.17.1 release
# Jinja2 version: 3.1.2
# Last messages:
#   reading sources... [ 50%] reference/app.crud.crud_role
#   reading sources... [ 51%] reference/app.crud.crud_team
#   reading sources... [ 53%] reference/app.crud.crud_user
#   reading sources... [ 54%] reference/app.db
#   reading sources... [ 56%] reference/app.db.init_db
#   reading sources... [ 57%] reference/app.db.session
#   reading sources... [ 59%] reference/app.initial_data
#   reading sources... [ 60%] reference/app.main
#   reading sources... [ 62%] reference/app.models
#   reading sources... [ 63%] reference/app.models.group
# Loaded extensions:
#   sphinx.ext.mathjax (5.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/mathjax.py
#   sphinxcontrib.applehelp (1.0.2) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/applehelp/__init__.py
#   sphinxcontrib.devhelp (1.0.2) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/devhelp/__init__.py
#   sphinxcontrib.htmlhelp (2.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/htmlhelp/__init__.py
#   sphinxcontrib.serializinghtml (1.1.5) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/serializinghtml/__init__.py
#   sphinxcontrib.qthelp (1.0.3) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/qthelp/__init__.py
#   alabaster (0.7.12) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/alabaster/__init__.py
#   sphinx.ext.todo (5.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/todo.py
#   sphinx.ext.viewcode (5.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/viewcode.py
#   sphinx.ext.autodoc.preserve_defaults (1.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/preserve_defaults.py
#   sphinx.ext.autodoc.type_comment (5.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/type_comment.py
#   sphinx.ext.autodoc (5.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py
#   sphinx.ext.napoleon (5.0.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/napoleon/__init__.py
#   sphinxcontrib.apidoc (0.3.0) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/apidoc/__init__.py
#   sphinxcontrib.autodoc_pydantic (1.7.1) from /Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/__init__.py
Traceback (most recent call last):
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/cmd/build.py", line 276, in build_main
    app.build(args.force_all, filenames)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/application.py", line 329, in build
    self.builder.build_update()
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/builders/__init__.py", line 288, in build_update
    self.build(to_build,
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/builders/__init__.py", line 302, in build
    updated_docnames = set(self.read())
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/builders/__init__.py", line 409, in read
    self._read_serial(docnames)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/builders/__init__.py", line 430, in _read_serial
    self.read_doc(docname)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/builders/__init__.py", line 483, in read_doc
    publisher.publish()
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/core.py", line 217, in publish
    self.document = self.reader.read(self.source, self.parser,
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/io.py", line 103, in read
    self.parse()
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/readers/__init__.py", line 78, in parse
    self.parser.parse(self.input, document)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/parsers.py", line 78, in parse
    self.statemachine.run(inputlines, document, inliner=self.inliner)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 170, in run
    results = StateMachineWS.run(self, input_lines, input_offset,
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 2769, in underline
    self.section(title, source, style, lineno - 1, messages)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 393, in new_subsection
    newabsoffset = self.nested_parse(
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 281, in nested_parse
    state_machine.run(block, input_offset, memo=self.memo,
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/statemachine.py", line 239, in run
    context, next_state, result = self.check_line(
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/statemachine.py", line 451, in check_line
    return method(match, context, next_state)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 2342, in explicit_markup
    nodelist, blank_finish = self.explicit_construct(match)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 2354, in explicit_construct
    return method(self, expmatch)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 2096, in directive
    return self.run_directive(
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/docutils/parsers/rst/states.py", line 2146, in run_directive
    result = directive_instance.run()
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/directive.py", line 148, in run
    documenter.generate(more_content=self.content)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 955, in generate
    self.document_members(all_members)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 831, in document_members
    documenter.generate(
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 1788, in generate
    return super().generate(more_content=more_content,
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 955, in generate
    self.document_members(all_members)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py", line 164, in document_members
    super().document_members(*args, **kwargs)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 1779, in document_members
    super().document_members(all_members)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 831, in document_members
    documenter.generate(
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinx/ext/autodoc/__init__.py", line 945, in generate
    self.add_directive_header(sig)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/directives/options/composites.py", line 259, in wrapped
    result = func(*args, **kwargs)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py", line 539, in add_directive_header
    self.add_default_value_or_marker()
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py", line 586, in add_default_value_or_marker
    if self.needs_required_marker:
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/directives/autodocumenters.py", line 549, in needs_required_marker
    is_required = self.pydantic.inspect.fields.is_required(field_name)
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/inspection.py", line 188, in is_required
    return self.get(field_name).required
  File "/Users/pipeknight/.pyenv/versions/3.8.13/lib/python3.8/site-packages/sphinxcontrib/autodoc_pydantic/inspection.py", line 137, in get
    return self.attribute[name]
KeyError: 'created_by'

My conf file:

# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import os
import sys

# Also we may load a dotenv file here
sys.path.insert(0, os.path.abspath('.'))


# -- Project information -----------------------------------------------------

project = 'fastapi-alembic-sqlmodel-async'
copyright = '2022, -'
author = '-'


# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    'sphinx.ext.todo',
    'sphinx.ext.viewcode',
    'sphinx.ext.autodoc',
    'sphinx.ext.napoleon',
    'sphinxcontrib.apidoc',
    'sphinxcontrib.autodoc_pydantic',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# Apidoc options
apidoc_module_dir = '../fastapi-alembic-sqlmodel-async'
apidoc_output_dir = 'reference'
apidoc_excluded_paths = ['tests', 'kafka_test']
apidoc_separate_modules = True
# apidoc_extra_args = ['-d 2']

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'alabaster'
# html_theme = 'sphinx-rtd-theme'

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

I have several models in my app with field named created_by, for example:

from sqlmodel import Field, Relationship, SQLModel

class Group(SQLModel, table=True):
    id: Optional[int] = Field(default=None, nullable=False, primary_key=True)
    updated_at: Optional[datetime]
    created_at: Optional[datetime]
    created_by_id: Optional[UUID] = Field(default=None, foreign_key="user.id")
    created_by: "User" = Relationship(  # noqa: F821
        sa_relationship_kwargs={
            "lazy": "select",
            "primaryjoin": "Group.created_by_id==User.id",
        }
    )

And for some reason this broke my docs generation. If I comment 'sphinxcontrib.autodoc_pydantic' in conf.py extensions, than everything works

@PipeKnight
Copy link
Author

So maybe it's the problem with supporting sqlmodel library in your extension, which is something like a bridge between pydantic and sqlalchemy

@mansenfranzen
Copy link
Owner

Thanks for reporting the issue here! It is very likely related to sqlmodel. There is a similar issue here #124. I will take a look at it soonish.

@mansenfranzen mansenfranzen self-assigned this Jun 5, 2022
@mansenfranzen mansenfranzen added the bug Something isn't working label Jun 5, 2022
@mansenfranzen
Copy link
Owner

Okay, this is related to what a Relationship represents in sqlmodel. In fact, it is not a field (or column) of a pydantic model (or sqlalchemy table).

autodoc_pydantic currently assumes all class attributes of a parent pydantic model to be a pydantic field which is simply wrong in this case. It will require an additional, more explicit check if a class attribute of a pydantic model parent is truly a pydantic field. Luckily, this is not too difficult to implement and it should actually prevent further unwanted side effects.

@mansenfranzen
Copy link
Owner

This bug is addressed via #130 .

Before merging the related PR, it would be great if you could test the bugfix on your site. To do so, please install the current dev release in your doc-building-environment via pip install git+https://github.com/mansenfranzen/autodoc_pydantic.git@v1.7.2-a.1 and rebuild your docs.

@mansenfranzen
Copy link
Owner

@all-contributors please add @PipeKnight for bug

@allcontributors
Copy link
Contributor

@mansenfranzen

I've put up a pull request to add @PipeKnight! 🎉

@mansenfranzen
Copy link
Owner

@PipeKnight This should be fixed with the current v1.7.2 release of today. Feel free to reopen if the issue persists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants