Skip to content

Make Sphinx document @cached_property on slotted classes#1517

Closed
clayote wants to merge 7 commits into
python-attrs:mainfrom
clayote:sphinx-hack
Closed

Make Sphinx document @cached_property on slotted classes#1517
clayote wants to merge 7 commits into
python-attrs:mainfrom
clayote:sphinx-hack

Conversation

@clayote

@clayote clayote commented Feb 20, 2026

Copy link
Copy Markdown

Slotted instances have no __dict__, but their class object does have __dict__, and that's the __dict__ that Sphinx looks at to find class members to document. So I put the @cached_property objects back into the class's __dict__ after the class object was instantiated, and Sphinx can document them just fine.

You can't use the @cached_property objects for anything else, since there's still no dictionary on any instances of the slotted class. So attrs' generated __getattr__ handles their functionality like normal.

Summary

Fixes #1325.

Pull Request Check List

  • Do not open pull requests from your main branch – use a separate branch!
    • There's a ton of footguns waiting if you don't heed this warning. You can still go back to your project, create a branch from your main branch, push it, and open the pull request from the new branch.
    • This is not a pre-requisite for your pull request to be accepted, but you have been warned.
  • Added tests for changed code.
    Our CI fails if coverage is not 100%.
  • Changes or additions to public APIs are reflected in our type stubs (files ending in .pyi).
    • ...and used in the stub test file typing-examples/baseline.py or, if necessary, typing-examples/mypy.py.
    • If they've been added to attr/__init__.pyi, they've also been re-imported in attrs/__init__.pyi.
  • Updated documentation for changed code.
    • New functions/classes have to be added to docs/api.rst by hand.
    • Changes to the signatures of @attr.s() and @attrs.define() have to be added by hand too.
    • Changed/added classes/methods/functions have appropriate versionadded, versionchanged, or deprecated directives.
      The next version is the second number in the current release + 1.
      The first number represents the current year.
      So if the current version on PyPI is 22.2.0, the next version is gonna be 22.3.0.
      If the next version is the first in the new year, it'll be 23.1.0.
      • If something changed that affects both attrs.define() and attr.s(), you have to add version directives to both.
  • Documentation in .rst and .md files is written using semantic newlines.
  • Changes (and possible deprecations) have news fragments in changelog.d.
  • Consider granting push permissions to the PR branch, so maintainers can fix minor issues themselves without pestering you.

The property objects do nothing, because they're not accessible from the instance -- slotted instances have no __dict__, but their *class object* does have __dict__, and that's the __dict__ that Sphinx looks at to find class members to document.
@clayote clayote changed the title Perform a very silly hack to make Sphinx document @cached_property Perform a very silly hack to make Sphinx document @cached_property on slotted classes Feb 20, 2026
@clayote clayote changed the title Perform a very silly hack to make Sphinx document @cached_property on slotted classes Make Sphinx document @cached_property on slotted classes Feb 20, 2026
@clayote clayote marked this pull request as ready for review February 20, 2026 21:32
@clayote

clayote commented Feb 21, 2026

Copy link
Copy Markdown
Author

This turns out not to work for @cached_property on any parent classes. I think I can apply the same principle there, with a little more hacking.

@clayote clayote marked this pull request as draft February 21, 2026 05:50
@clayote

clayote commented Feb 21, 2026

Copy link
Copy Markdown
Author

No, actually, that's intended behavior in Sphinx. I wanted some of the parent class's methods to be documented on the child class, but that's a me problem.

@clayote clayote marked this pull request as ready for review February 21, 2026 06:42
@hynek

hynek commented Feb 21, 2026

Copy link
Copy Markdown
Member

How can we test this does anything at all? Maybe add an example to the docs somewhere?

clayote and others added 3 commits February 22, 2026 04:02
It turns out that the new class object's `__dict__` is only sometimes
a proxy to the actual dictionary object we pass in.
@clayote

clayote commented Feb 21, 2026

Copy link
Copy Markdown
Author

@hynek It turns out not to be that hard to run Sphinx in a unit test. I've added one. Good thing, too, because I found that the way I was messing with the class dictionary was brittle. Thanks!

@codspeed-hq

codspeed-hq Bot commented Feb 21, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 50.42%

⚡ 1 improved benchmark
❌ 1 regressed benchmark
✅ 13 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_first_access 6.4 ms 1.4 ms ×4.6
test_repeated_access 238.5 µs 481 µs -50.42%

Comparing clayote:sphinx-hack (e517b48) with main (c44b8b0)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (fb9ea67) during the generation of this report, so c44b8b0 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@clayote clayote marked this pull request as draft February 21, 2026 16:04
@clayote

clayote commented Feb 21, 2026

Copy link
Copy Markdown
Author

My understanding of what class __dict__ does was flawed. This approach won't work consistently.

On the other hand, I've found a Sphinx feature that will do alright...

@clayote clayote closed this Feb 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

slots + cached_property + sphinx

2 participants