Skip to content

Add Sphinx documentation (#30)#56

Merged
ericof merged 13 commits into
mainfrom
docs-sphinx-30
Jul 13, 2026
Merged

Add Sphinx documentation (#30)#56
ericof merged 13 commits into
mainfrom
docs-sphinx-30

Conversation

@jensens

@jensens jensens commented Jul 13, 2026

Copy link
Copy Markdown
Member

Implements #30. Design discussed in this comment.

Important

Merge #55 first. Once the docstrings become the source of truth for the reference, every error in them gets published verbatim — and I confirmed that concretely: built against main without #55, the generated reference contains isinstance(fti, IDexterityFTI) (always False), toc.getTerm (undefined name), and def test_fti(self, ...) (fails with fixture 'self' not found). With #55 merged in locally, the same build renders the corrected examples. This PR is otherwise independent and does not conflict.

Why sphinx-autodoc2 and not sphinx.ext.autodoc

Two properties of this package rule out the obvious choice:

  1. @pytest.fixture does not return a function. It returns a FixtureFunctionDefinition. sphinx.ext.autodoc introspects live objects, so autofunction cannot document a single one of our fixtures.
  2. Our docstrings are Markdown. sphinx.ext.autodoc hands docstrings to the reStructuredText parser. MyST-parser's own documentation states it is not compatible with MyST docstrings.

sphinx-autodoc2 analyses the source statically. The decorator becomes irrelevant, and autodoc2_docstring_parser_regexes parses the docstrings as MyST. Both problems disappear rather than getting worked around.

Two configuration gotchas are recorded in docs/conf.py because they are not obvious: autodoc2_packages[…]["path"] must be relative to conf.py, and the fieldlist MyST extension is required or :param x: renders as literal text.

The docs build needs no Plone

This falls out of the static analysis, and it is the nicest consequence.

The docs environment installs from docs/requirements.txt — Sphinx, the theme, autodoc2 — and deliberately does not install pytest-plone itself, because doing so would drag in Products.CMFPlone[test] and the rest of the runtime dependencies for no benefit.

A cold make docs, creating the virtualenv and installing everything from scratch, takes 3.8 seconds. The CI job skips make install entirely. It also means Read the Docs would not have to install Plone, which is worth weighing when we settle the hosting question.

Contents

Diataxis, all four quadrants:

Section Content
tutorials/first-test.md One guaranteed path: conftest → passing test → content via marker.
how-to/ Set up the plugin; test add-on install and uninstall; test a REST API; speed up a slow suite.
explanation/ Why pytest for Plone. Testing layers, scopes, and isolation — including the trap behind #49/#50, which is documented nowhere today.
reference/ Every fixture (curated groups + autodoc2-object per fixture), the portal marker, the fixtures_factory API.

reference/fixtures.md is the hybrid the Plone docs standard asks for: the grouping and the overview table are written by hand, because autodoc cannot produce navigation; the facts come from the docstrings.

Verification

  • make docs builds with -W (warnings are errors): 0 warnings.
  • All 24 fixtures render, with signatures, resolved type annotations, and their examples as highlighted code blocks.
  • ruff format --diff and ruff check --diff clean.

Not in this PR

  • Slimming the README. It happens next, once these docs are complete and hosted — gutting it first would leave users worse off than today.
  • Hosting (RTD vs. submodule into plone/documentation) and Vale. Both deserve their own issues; bundling Vale would mean bringing 620 lines of existing prose to Microsoft style, which is a project in itself.

🤖 Generated with Claude Code

jensens added 5 commits July 13, 2026 11:24
Structured with Diataxis: a tutorial, four how-to guides, two explanation
pages, and a reference for every fixture, the portal marker, and the
fixtures_factory API.

The reference is generated from the docstrings by sphinx-autodoc2 rather
than sphinx.ext.autodoc. Two things make that necessary:

- @pytest.fixture returns a FixtureFunctionDefinition, not a function.
  sphinx.ext.autodoc introspects live objects and cannot document our
  fixtures. sphinx-autodoc2 analyses the source statically, so the
  decorator is irrelevant to it.
- Our docstrings are Markdown. sphinx.ext.autodoc hands docstrings to the
  reStructuredText parser, which mangles them. autodoc2 parses them as
  MyST.

Because the analysis is static, the docs build never imports the package
and needs no Plone at all. It gets its own small virtualenv from
docs/requirements.txt and takes seconds, not minutes.

make docs builds with -W, so warnings are errors.
The job deliberately skips 'make install'. Because sphinx-autodoc2
analyses the source statically and never imports the package, the
documentation builds without Plone. A cold build -- creating the
virtualenv and installing every dependency from scratch -- takes under
four seconds, against roughly three minutes for a test job.

make docs runs sphinx with -W, so any warning fails the build.
The 'requirements*.txt' rule exists for the mxdev-generated
requirements-mxdev.txt at the repository root, but it also swallowed
docs/requirements.txt, which is hand-written and must be committed. The
docs CI job failed with 'No rule to make target docs/requirements.txt'
because the file was simply not in the checkout.
The documentation assumed the reader already had a testing.py with
layers: MY_ADDON_FUNCTIONAL_TESTING simply appeared out of nowhere in
the conftest examples. Explaining the layer machinery is out of scope
here, but leaving it unexplained is not acceptable either.

The tutorial and the setup how-to now state the prerequisite plainly and
point at plone.app.testing and plone.testing, which own that machinery.
The explanation page now says that layers come from your add-on's
testing.py and that pytest-plone consumes rather than replaces them.

Links go to the READMEs on GitHub. Those are the current source of
truth: the Plone 6 documentation has no backend testing chapter at all,
and the only rendered HTML lives on the legacy 5.docs.plone.org domain.
jensens and others added 8 commits July 13, 2026 12:17
Reported in #16, where it was never actually answered: widening a
working function-scoped fixture to scope='class' and reading
functional_class['portal'] raises KeyError: 'portal'.

The layer sets its portal key in testSetUp, which zope.pytestlayer
invokes only for the function-scoped fixture. At class scope it never
runs, so the key does not exist. Changing a pytest scope is normally a
drop-in change, which is precisely why this bites.

The explanation page now covers why the two models do not line up, and
the how-to guide warns against it at the point where a reader is tempted
to do it -- they should use portal_class or functional_portal_class,
which drive that lifecycle themselves.

Verified: the KeyError reproduces exactly as described.
Move the documentation dependencies from docs/requirements.txt into a
[docs] optional-dependency group and build with the project's own
environment (-e .[test,docs]). conf.py reads the version from
pytest_plone.__version__ and gains a logo, favicon, sitemap, OpenGraph
tags, a 404 page, and a live-reload docs-livehtml Makefile target.

Extract the docs build into a reusable .github/workflows/docs.yml that
builds the HTML and deploys to GitHub Pages on main, and add Vale prose
linting (.vale.ini, docs/_styles, docs-vale target).
The Makefile selects the Plone constraints from the PLONE_VERSION
environment variable (defaulting to 6.1.4). The docs refactor had renamed
the CI env vars to lowercase, so make install stopped seeing them and
every job silently installed against 6.1.4 — which pins lxml 5.4.0, a
version with no Python 3.14 wheel, breaking the 3.14 test job.

Restore the uppercase env var names so each matrix job installs against
its own Plone version, and pass PLONE_VERSION/PYTHON_VERSION into the
docs workflow's install step.
In CI only, the requirements-mxdev.txt recipe drops the docs extra from
mx.ini (installing .[test] instead of .[test,docs]) unless CI_DOCS is
set, so the test and coverage jobs skip the heavy documentation
dependencies. mx.ini is edited only for the mxdev run and restored right
after, and local runs (CI unset) are never touched. The docs workflow
sets CI_DOCS=1 so its build keeps Sphinx and the rest of the docs stack.
Make Vale lint only the documentation sources, not the generated
docs/_build output, which was doubling every alert. Add the false-positive
terms flagged by Vale to the vocabulary (finalizer, teardown, reimplement,
and the plone.*/zope.* package names), tighten the spaced em-dashes to
comply with the Microsoft style, code-format pytest-plone in the two
headings, and reword the em-dashes glued to inline code that Vale cannot
otherwise accept. The docs workflow now runs the Vale check (check-vale:
true); it fails only on errors, so the remaining style warnings are shown
but non-blocking.
Add the app_class and functional_app_class fixtures to the reference,
plus a Distribution section covering create_site and its supporting
distribution_name, answers, site_logo, site_owner_name, and
site_owner_password fixtures, and list them all in the overview table.

@ericof ericof left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added new fixtures and did use make install to make my life easier ;-)

@ericof

ericof commented Jul 13, 2026

Copy link
Copy Markdown
Member

@jensens Thank you for this

@ericof
ericof merged commit f99bd89 into main Jul 13, 2026
17 checks passed
@ericof
ericof deleted the docs-sphinx-30 branch July 13, 2026 22:03
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.

2 participants