Skip to content

[py] Publish a machine-readable API reference and ship guidance in the wheel - #17906

Open
AutomatedTester wants to merge 2 commits into
trunkfrom
copse/review-the-documentation-creation-in-this-f927d8
Open

[py] Publish a machine-readable API reference and ship guidance in the wheel#17906
AutomatedTester wants to merge 2 commits into
trunkfrom
copse/review-the-documentation-creation-in-this-f927d8

Conversation

@AutomatedTester

@AutomatedTester AutomatedTester commented Aug 12, 2026

Copy link
Copy Markdown
Member

🔗 Related Issues

None yet. The review that motivated this is committed alongside it as docs/plans/llm-facing-documentation.md; This will be removed before merging

💥 What does this PR do?

A growing share of Selenium's documentation is read by machines rather than people — during training, during retrieval, and live over HTTP when a coding agent is asked to "write a Selenium test". When that reading goes badly the model falls back on stale priors and emits DesiredCapabilities, an executable_path, a third-party driver manager, and a sleep where a wait belongs. Users then attribute that code to Selenium.

This implements the Python tracks of the attached plan. The plan itself covers all five bindings; nothing here changes any other binding.

Canonical URL. Three copies of the Python API docs compete for recall — the release build on selenium.dev, the per-commit Read the Docs preview, and unofficial mirrors that outrank both — and none of them declared which was authoritative. Sets html_baseurl, so every page (including the Read the Docs build) carries rel=canonical pointing at the released reference. Rewrites the index wording to name that build as canonical and Read the Docs as a preview of the next release.

Machine-readable output, all at stable URLs under /selenium/docs/api/py/:

File Source
llms.txt new Sphinx extension, py/docs/source/_ext/llm_assets.py
sitemap.xml same extension
objects.inv already emitted by Sphinx; now documented rather than discarded
deprecations.json new py/generate_deprecations.py

deprecations.json currently publishes 16 deprecated APIs, each with the replacement to use instead, parsed straight out of the warnings.warn messages. That mapping previously existed only as prose inside function bodies, where nothing but a running program could read it.

In-package guidance. selenium/llms.txt now ships inside the wheel, so a tool with the package on disk finds the project's own guidance instead of inferring it. The published llms.txt reads its rules back out of that same file, so there is one copy rather than two to drift apart.

🔧 Implementation Notes

deprecations.json is committed, not just generated. It lives in py/docs/source/_extra/ and is published verbatim via html_extra_path. Committing it means a PR that deprecates something shows the deprecation in its diff, and it means bazel build //py:docs works on a clean checkout. A unit test fails if it stops matching the source. This needed narrow negations in .gitignore, which otherwise ignores everything under py/docs/source/ to keep the autosummary stubs out.

viewcode is replaced by linkcode — the two conflict, so this is a choice rather than an addition. viewcode rendered ~140 modules of source into the site as a second, non-canonical copy of what is already on GitHub, which is the same duplicate-content problem this PR is otherwise trying to fix. linkcode_resolve links each object to GitHub at the release tag with a line anchor. This does delete published pages (_modules/**) — nothing in the repo references them, but it is the one change here that removes rather than adds, and it is easy to revert on its own if maintainers disagree.

intersphinx adds a network fetch to the docs build. Inventories for the stdlib, trio and urllib3 are fetched at build time. Timeout is 5s and a failure degrades to plain literals rather than erroring, but it is a new network dependency for bazel build //py:docs and worth a maintainer's opinion.

The guidance rules themselves are deliberately versioned — each says which release the old form stopped being correct in (executable_path 4.10, find_element_by_* 4.3, options.headless 4.9), because a reader that has absorbed a decade of blog posts needs the version to know which memory to discard.

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude Code (Opus 5)
    • What was generated: the documentation review in docs/plans/llm-facing-documentation.md, and a first pass at all of the Python changes — the Sphinx extension, the deprecation extractor, the conf.py changes, the unit tests, and the prose in py/selenium/llms.txt.
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

Not verified in CI-equivalent conditions. bazel test //py:unit and bazel build //py:docs could not run on my machine: both fail during analysis fetching mocha from registry.npmjs.org with a TLS certificate error, because //py:selenium depends transitively on the JS atoms. That is a local network problem, not a code one, but it means the Sphinx build itself is unverified and CI is the first real run of it.

What was verified: ruff check and ruff format pass; buildifier produces no changes; bazel query confirms the new targets resolve and that //py:docs picks up _ext/_extra; bazel run //py:generate-deprecations runs end to end. All 19 cases in the two new test files pass when run directly under Python 3.11.

Follow-up work, from the plan:

  • The other four bindings need the same treatment (Track C), and their deprecations.json files need merging into one cross-binding dataset (D1).
  • docs/api/llms.txt and a real landing page at /selenium/docs/api/ (A1, A2) need all five bindings first.
  • Linking the API reference from the site llms.txt, and a Sitemap: reference for /selenium/docs/api/, are changes in SeleniumHQ/seleniumhq.github.io (B2, A4).
  • Docs only regenerate at release, so this is invisible for a full cycle until nightly publishing exists (F1).

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

…achines

A growing share of Selenium's documentation is read by models rather than
people: during training, during retrieval, and live over HTTP when a coding
agent is asked to write a Selenium test. When that reading goes badly the
model falls back on stale priors and emits DesiredCapabilities, an
executable_path, a third-party driver manager, and a sleep where a wait
belongs — and users attribute that code to Selenium.

Reviews what this repository publishes today across all five bindings and
sets out six tracks of work to fix it, with the waves that can run in
parallel. Findings include: the API reference has no machine-readable entry
point and is unlinked from the site llms.txt; objects.inv, element-list and
xrefmap.yml are generated and then discarded; the Python docs have three
competing copies and no canonical URL; deprecation markers exist in every
binding but the "gone, use this instead" mapping exists nowhere consumable;
and nothing LLM-facing ships inside the wheel, gem, jar, npm package or
nupkg.
…e wheel

The Python API reference is increasingly read by machines — crawlers,
retrieval pipelines, and coding agents fetching it live — and it offered them
no entry point, no canonical URL, and no way to tell a current API from one
removed several releases ago. Three copies of the Python docs compete for
recall, none of them declaring which is authoritative.

Canonical URL: sets html_baseurl so every page, including the per-commit
Read the Docs preview, carries rel=canonical pointing at the released
reference. Rewrites the index wording to name that build as canonical and
Read the Docs as a preview of the next release.

Machine-readable output: a new Sphinx extension writes llms.txt and
sitemap.xml into the HTML root; objects.inv is documented at its stable URL;
and generate_deprecations.py parses the sources with ast to emit
deprecations.json, publishing 16 deprecated APIs with the replacement to use
instead. Swaps viewcode for linkcode so source links point at GitHub at the
release tag rather than at a second, non-canonical copy of the source
rendered into the site.

In-package guidance: selenium/llms.txt ships inside the wheel, so a tool with
the package on disk finds the project's own guidance instead of inferring it.
The published llms.txt reads its rules back out of that file, leaving one
copy rather than two to drift apart.

Implements the Python tracks of docs/plans/llm-facing-documentation.md.
@selenium-ci selenium-ci added C-py Python Bindings B-build Includes scripting, bazel and CI integrations labels Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations C-py Python Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants