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

AttributeError assigning __annotations__ on read-only class #8883

Closed
baldurk opened this issue Feb 14, 2021 · 1 comment
Closed

AttributeError assigning __annotations__ on read-only class #8883

baldurk opened this issue Feb 14, 2021 · 1 comment

Comments

@baldurk
Copy link

baldurk commented Feb 14, 2021

Describe the bug
I recently updated to sphinx 3.5.0 and I started seeing an AttributeError being raised when building on this line:

parent.__annotations__ = annotations

Upon investigation it seems related to the area of #8799 which removed try/except around the assignment of __annotations__, though I think that code is new since the previous version anyway so that might be a bad lead.

I'm not too familiar with python but it looks like it's possible to get AttributeError when assigning __annotations__ if the attribute isn't already there previously. In my case this class is imported from C (generated by swig) so I'm not sure if this is possible to replicate in pure python. Looking around it seems like if you set __slots__ then it will cause AttributeError when adding new attributes, but as far as I could see on the sphinx side this code only runs on C created classes because I think native python class attributes will show up as properties, not attributes.

To Reproduce
Unfortunately my project is not trivial to build since it requires C++. I've added the steps to reproduce the behavior on my project here, but if necessary I can set up a simple C project with no dependencies to reproduce:

# need dependencies for compiling the C++ code, might need to adjust this line or the packages depending on your distro
$ apt-get install gcc g++ cmake python3-dev bison autoconf automake libpcre3-dev

$ git clone https://github.com/baldurk/renderdoc
$ cd renderdoc

# do the C++ side build with as much turned off as possible. Still triggers the bug but avoids extra build-time dependencies
$ cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_GL=OFF -DENABLE_GLES=OFF -DENABLE_VULKAN=OFF -DENABLE_XLIB=OFF -DENABLE_XCB=OFF -DENABLE_RENDERDOCCMD=OFF -DENABLE_QRENDERDOC=OFF -Bbuild -H.

# customise -j4 depending on number of threads/cores on your system
$ make -C build -j4

$ cd docs
$ make html

If you want to make a fix and have me test it rather than repro'ing yourself, I'm happy to do that too.

Expected behavior
The documentation should build without throwing an exception, and handle this case where classes can't have new attributes added.

Your project
The sphinx project is available at https://github.com/baldurk/renderdoc under the 'docs' subdirectory, but as above it depends on the C++ project having built the python libraries.

Environment info

  • OS: Linux commands above but this also happens for me on windows.
  • Python version: Tested on 3.6.0 and 3.6.9
  • Sphinx version: 3.5.0
  • Sphinx extensions: sphinx.ext.autodoc. I also have a local extension sphinx_paramlinks to allow :paramref: but I don't think it's related.
  • Extra tools: None, fails during build

Additional context
I linked above the first failing CI run (after I revealed the actual exception that was previously hidden by mistake): https://github.com/baldurk/renderdoc/runs/1897925549?check_suite_focus=true

After forcing sphinx 3.4.0 when pip installing, it worked correctly: https://github.com/baldurk/renderdoc/runs/1897997876?check_suite_focus=true

Full exception log from a local build in a clean docker container
root@155a990f7394:/renderdoc/docs# cat /tmp/sphinx-err-eubtp5yu.log
# Sphinx version: 3.5.0
# Python version: 3.6.9 (CPython)
# Docutils version: 0.16 release
# Jinja2 version: 2.11.3
# Last messages:
#   reading sources... [ 52%] python_api/examples/renderdoc/save_texture
#
#   reading sources... [ 53%] python_api/examples/renderdoc_intro
#
#   reading sources... [ 54%] python_api/index
#
#   reading sources... [ 55%] python_api/qrenderdoc/config
#
#   The name of the builder is: html
#
# Loaded extensions:
#   sphinx.ext.mathjax (3.5.0) from /usr/local/lib/python3.6/dist-packages/sphinx/ext/mathjax.py
#   sphinxcontrib.applehelp (1.0.2) from /usr/local/lib/python3.6/dist-packages/sphinxcontrib/applehelp/__init__.py
#   sphinxcontrib.devhelp (1.0.2) from /usr/local/lib/python3.6/dist-packages/sphinxcontrib/devhelp/__init__.py
#   sphinxcontrib.htmlhelp (1.0.3) from /usr/local/lib/python3.6/dist-packages/sphinxcontrib/htmlhelp/__init__.py
#   sphinxcontrib.serializinghtml (1.1.4) from /usr/local/lib/python3.6/dist-packages/sphinxcontrib/serializinghtml/__init__.py
#   sphinxcontrib.qthelp (1.0.3) from /usr/local/lib/python3.6/dist-packages/sphinxcontrib/qthelp/__init__.py
#   alabaster (0.7.12) from /usr/local/lib/python3.6/dist-packages/alabaster/__init__.py
#   sphinx.ext.autodoc.type_comment (3.5.0) from /usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/type_comment.py
#   sphinx.ext.autodoc (3.5.0) from /usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py
#   sphinx_paramlinks (unknown version) from /renderdoc/docs/sphinx_exts/sphinx_paramlinks/__init__.py
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/sphinx/cmd/build.py", line 280, in build_main
    app.build(args.force_all, filenames)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/application.py", line 352, in build
    self.builder.build_update()
  File "/usr/local/lib/python3.6/dist-packages/sphinx/builders/__init__.py", line 298, in build_update
    len(to_build))
  File "/usr/local/lib/python3.6/dist-packages/sphinx/builders/__init__.py", line 310, in build
    updated_docnames = set(self.read())
  File "/usr/local/lib/python3.6/dist-packages/sphinx/builders/__init__.py", line 417, in read
    self._read_serial(docnames)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/builders/__init__.py", line 438, in _read_serial
    self.read_doc(docname)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/builders/__init__.py", line 478, in read_doc
    doctree = read_doc(self.app, self.env, self.env.doc2path(docname))
  File "/usr/local/lib/python3.6/dist-packages/sphinx/io.py", line 221, in read_doc
    pub.publish()
  File "/usr/local/lib/python3.6/dist-packages/docutils/core.py", line 218, in publish
    self.settings)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/io.py", line 126, in read
    self.parse()
  File "/usr/local/lib/python3.6/dist-packages/docutils/readers/__init__.py", line 77, in parse
    self.parser.parse(self.input, document)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/parsers.py", line 104, in parse
    self.statemachine.run(inputlines, document, inliner=self.inliner)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 171, in run
    input_source=document['source'])
  File "/usr/local/lib/python3.6/dist-packages/docutils/statemachine.py", line 242, in run
    context, state, transitions)
  File "/usr/local/lib/python3.6/dist-packages/docutils/statemachine.py", line 459, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 2769, in underline
    self.section(title, source, style, lineno - 1, messages)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 395, in new_subsection
    node=section_node, match_titles=True)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 282, in nested_parse
    node=node, match_titles=match_titles)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.6/dist-packages/docutils/statemachine.py", line 242, in run
    context, state, transitions)
  File "/usr/local/lib/python3.6/dist-packages/docutils/statemachine.py", line 459, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 2769, in underline
    self.section(title, source, style, lineno - 1, messages)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 327, in section
    self.new_subsection(title, lineno, messages)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 395, in new_subsection
    node=section_node, match_titles=True)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 282, in nested_parse
    node=node, match_titles=match_titles)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 196, in run
    results = StateMachineWS.run(self, input_lines, input_offset)
  File "/usr/local/lib/python3.6/dist-packages/docutils/statemachine.py", line 242, in run
    context, state, transitions)
  File "/usr/local/lib/python3.6/dist-packages/docutils/statemachine.py", line 459, in check_line
    return method(match, context, next_state)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 2342, in explicit_markup
    nodelist, blank_finish = self.explicit_construct(match)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 2354, in explicit_construct
    return method(self, expmatch)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 2097, in directive
    directive_class, match, type_name, option_presets)
  File "/usr/local/lib/python3.6/dist-packages/docutils/parsers/rst/states.py", line 2146, in run_directive
    result = directive_instance.run()
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/directive.py", line 167, in run
    documenter.generate(more_content=self.content)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 1728, in generate
    all_members=all_members)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 970, in generate
    self.document_members(all_members)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 1717, in document_members
    super().document_members(all_members)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 853, in document_members
    check_module=members_check_module and not isattr)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 904, in generate
    if not self.import_object():
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 2455, in import_object
    self.update_annotations(self.parent)
  File "/usr/local/lib/python3.6/dist-packages/sphinx/ext/autodoc/__init__.py", line 2432, in update_annotations
    parent.__annotations__ = annotations
AttributeError: type 'qrenderdoc.PersistantConfig' has no attribute '__annotations__'

Let me know if you need any other information or a better repro case.

@owillebo
Copy link

I confirm this issue with the code base of my customer. We have a special 'Const' class that doesn't allow for attributes to be changed.
sphinx-err-ff203rri.log

@jfbu jfbu added this to the 3.5.1 milestone Feb 15, 2021
tk0miya added a commit to tk0miya/sphinx that referenced this issue Feb 15, 2021
tk0miya added a commit that referenced this issue Feb 16, 2021
…nnotations

Fix #8883: autodoc: AttributeError on assigning __annotations__
@tk0miya tk0miya closed this as completed Feb 16, 2021
This was referenced Mar 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants