Skip to content

docs: fix broken examples, fill gaps, and consolidate contributor guides - #603

Open
ffumero2003 wants to merge 12 commits into
Backblaze:masterfrom
ffumero2003:documentation-issues-584-601
Open

docs: fix broken examples, fill gaps, and consolidate contributor guides#603
ffumero2003 wants to merge 12 commits into
Backblaze:masterfrom
ffumero2003:documentation-issues-584-601

Conversation

@ffumero2003

Copy link
Copy Markdown

Summary

Documentation-only work across 13 of the issues filed as #584#601. Twelve are closed here; one is
partially addressed. 30 files changed, +755/−188, in 12 commits.

The only non-documentation file touched is b2sdk/_internal/replication/monitoring.py, where five
lines of a docstring were reindented. No executable code changes — see Notes for reviewers.


Closed by this PR

Corrections: documented behavior that was wrong

#584 — Quick Start examples did not run. doc/source/quick_start.rst used a nonexistent
EncryptionSettings class (the real name is EncryptionSetting), passed id= to EncryptionKey
instead of key_id=, gave BasicSyncEncryptionSettingsProvider a single mapping when its signature
requires two (read_bucket_settings and write_bucket_settings), and documented file_infos= where
Bucket.upload_local_file takes file_info=. Anyone copying the first examples in the guide got a
NameError or TypeError immediately. Note the issue mentions one EncryptionKey(id=...); there
were two, at lines 57 and 197.

#585 — Retention settings passed to the legal-hold method. Three calls handed a
FileRetentionSetting to B2Api.update_file_legal_hold, whose signature takes a LegalHold. Two of
them sat in an "equivalent to" block directly beneath correct update_retention calls, so the guide
contradicted itself. Repointed at update_file_retention.

#586 — Stale names in the Advanced guide. The prose referred to LocalUploadSource, which is not
exported; the surrounding examples already used the correct UploadSourceLocalFile. Separately, one
example called planner.create_file(...) while every other example in that section calls
bucket.create_file(...), and planner is never defined in the documented workflow. Scope note: only
the prose was wrong, not the code samples, so this is narrower than the issue implies.

#588 — A documented command that does not exist. contributing.rst advertised a deploy nox
session for publishing to PyPI. noxfile.py defines no such session. Publishing is done by
.github/workflows/cd.yml on a pushed version tag, which is now described instead.

#589 — Wrong CI matrix. Both contributor guides claimed "a matrix of 8 versions of Python."
PYTHON_VERSIONS in noxfile.py lists six interpreters (CPython 3.10–3.14 plus PyPy 3.10). Both
files also stated the matrix correctly further down, so each contradicted itself. Replaced the
hard-coded count with a reference to PYTHON_VERSIONS so it cannot drift again, and corrected
"Mac OS X" to "macOS".

#590 — Development install ignored the local checkout. The Installation Guide said to check out
the repository and then run pip install b2sdk, which resolves the published package from PyPI. A
developer testing their own changes would silently test a release instead. Now uses pip install -e .
with an explanation of why, and links to the Contributors Guide for the full nox/uv setup.

#591 — Typos and a stale claim. Fixed overalapping, manullay, "Streams that contains" and
"If all candidates fails" in advanced.rst. Replaced the front-page claim that the SDK "has been
developed for 3 years before it version 1.0.0 was released" — a time-relative statement that had gone
stale, plus an it/its error.

New documentation

#592 — Where a new test belongs. The repository keeps b2sdk.v0 through v3 working
simultaneously, and nox -s unit runs the suite once per interface version. Nothing explained how
test/unit/conftest.py selects tests, so a regression test could land in a single version directory
while the changed _internal behavior is shared by all four. Adds a decision table covering the
shared topic directories, test/unit/v0v3, v_all and test/integration; documents the --api
option, the apiver_deps mechanism and the apiver marker with from_ver/to_ver; and adds a
checklist for _internal changes.

#593 — Release runbook. README.release.md was three lines: run make_release_commit and follow
the output. Now documents the clean-worktree requirement, the X.Y.Z version format, reviewing
changelog.d fragments, and the commit/CI/tag sequence. Three boundaries were undocumented and are
now called out explicitly: that make_release_commit builds the changelog but neither commits nor
tags
despite its name; that CI must be green before tagging because a PyPI version cannot be reused;
and that CD creates a GitHub release but publishes to PyPI only when B2_PYPI_PASSWORD is present
and the tag is not a prerelease, so a green CD run is not proof that PyPI was updated. Also covers
recovery before and after a tag is pushed, including that towncrier build deletes the fragment files.

#595 — Replication was entirely undocumented. b2sdk.v3 exports seven replication classes with
implementation and tests in the repository, but there was no guide and no API reference, so the feature
was discoverable only by guessing at names. Adds doc/source/replication.rst covering setup via
ReplicationSetupHelper and by hand, the application-key capabilities each side requires, the four
ReplicationStatus values, monitoring a rule, reading a scan report, and interpreting partial results.
Adds four API reference pages under doc/source/api/replication/.

#601 — Embedded TODO directives. Six todo:: directives were invisible in published builds
(todo_include_todos is gated behind B2_SPHINX_TODO) and carried no owner or acceptance criteria.
Note the issue's own search pattern misses the uppercase .. TODO:: in advanced.rst. Resolved as:
two obsolete and removed (delete doc/source/b2sdk? — that directory does not exist; describe raw_simulator in detail — it already has a full autodoc page); one done (the "Why use b2sdk?" list is
now consistently phrased, with eight of ten bullets linking to what they describe); two turned into
real documentation (api/progress.rst and api/exception.rst, below); and one, an API design request,
split out to #602 with the directive retained and linked.

The progress page now distinguishes the two separate mechanisms — per-transfer listeners versus
SyncReport for sync — and documents two easily-missed contract details: set_total_bytes may be
called more than once on retry, and bytes_completed receives a running total that can decrease
when a transfer restarts. The exception page documents how to catch, warns against naming parent
classes because the hierarchy may gain intermediate classes in a compatible release, and covers the
retry advice carried on the errors themselves.

Consolidation

#599 — Two contributor guides had drifted. CONTRIBUTING.md and doc/source/contributing.rst
independently duplicated setup, sessions, testing and documentation commands. The Sphinx page carried
the nonexistent deploy session; the Markdown file carried dependency, lockfile and changelog guidance
the Sphinx page lacked. Following the issue's preference for "a short GitHub-friendly entry point while
keeping the full published contributor guide discoverable," the unique Markdown content (versioning,
changelog fragments, dependency management, keyword-filtered tests) moved into the Sphinx guide, which
is now canonical, and CONTRIBUTING.md is reduced to an entry point that links to it. The
_contributors_guide label is preserved because install.rst now references it.

Worth noting the alternative in that issue — generating one file from the other — is not available
without adding myst-parser, which is not in the Sphinx extensions list. So consolidation was the only
documentation-only path.


Partially addressed

#597 — Classifying the b2sdk.v3 exports. The inventory in the issue is accurate: 208
non-underscored names, 137 of which appear nowhere in the .rst sources. annotations is among them,
leaked from from __future__ import annotations — a concrete example of an accidental re-export.

This PR delivers the durable half: api_types.rst now states explicitly that being importable from a
versioned namespace is not a compatibility promise, and gives the rule for telling names apart —
present in Public API, present in Internal API, or in neither and carrying no promise. The mechanism
already existed (api_reference.rst has separate Public and Internal toctrees, and RawSimulator is
already documented as internal), so this completes an existing scheme rather than inventing one.

Not delivered: the per-name classification of the remaining 137. That requires a maintainer to decide
which are intentionally public, and publishing a guess would be worse than publishing nothing.
Committed as Refs #597, so the issue stays open.


Deliberately not addressed

#587 — SSE-B2 encryption defaults. Blocked, and not only on timing. The issue was written against
an encryption-defaults branch and states that omitting encryption yields SSE-B2. In the change as
written, that appears to hold in RawSimulator — whose implicit bucket default moves from NONE to
SSE_B2_AES — while what is enforced client-side is narrower: that plaintext may not be requested
for a bucket default or a file write. Whether real B2 returns SSE-B2 for an omitted default is server
behavior the SDK cannot guarantee. Documenting it as a promise could commit the SDK to something it
does not control, so this needs confirming with whoever owns the server-side change first.

Concretely, doc/source/server_side_encryption.rst:12 currently says the argument means relying on
bucket defaults "for SSE-B2 and for no encryption," and
doc/source/api/encryption/setting.rst:18-20 documents SSE_NONE as the "commonly used" no-encryption
setting. Both need revising once the behavior is settled. This PR leaves them untouched rather than
guessing.

#594 — v0–v3 migration matrix. The encryption change spans all four interfaces, so a matrix written
now would ship incomplete and need rewriting. Better done once that lands.

#596 — Event Notifications. Documenting a Private Preview feature requires knowing its current
release status and stability expectations, which is a product question that cannot be answered from the
repository.

#598 — Executing documentation examples in CI. Not documentation work. sphinx.ext.doctest is
absent from conf.py, and the doc session runs plain sphinx-build -b html -T -W, which fails on
warnings but never executes a code block. Closing it means changes to conf.py, noxfile.py and CI
configuration. Worth relabeling so it is not waiting on a documentation pass. It would have caught #584.

#600 — Upstream and fork URLs. Needs an ownership decision before any edit, exactly as the issue
says. Note the release tooling already hardcodes Backblaze/b2-sdk-python as UPSTREAM_REPO_URL in
noxfile.py, so the current setup assumes upstream. Also relevant: pyproject.toml:167-168 are
Towncrier's title_format and issue_format, which control the links in every generated CHANGELOG
entry, so that part is release infrastructure rather than documentation.

#602prioritize should accept an enum (filed from #601). An API design change, not
documentation.


Notes for reviewers

The one Python file. b2sdk/_internal/replication/monitoring.py has five docstring lines
reindented. ReplicationMonitor's :param: continuation lines were not indented, so docutils reported
"Field list ends without a blank line" as soon as the class was added to the API reference, and the -W
build failed. Comparing the parsed AST with docstrings excluded shows the executable code is identical.
That docstring still refers to b2sdk.v2 types throughout; left alone as out of scope.

ReplicationReport is described in prose rather than autodoc. It inherits
counter_by_status: Counter and samples_by_status_*: dict[..., tuple[FileVersion, ...]] from
CountAndSampleScanReport in scan/scan.py, which imports both names, but
sphinx-autodoc-typehints resolves inherited annotations against the subclass's module, and
monitoring.py imports neither. Adding those imports fixes the docs build but fails ruff with F401,
since the module's own code never references them. Rather than adding two # noqa imports to satisfy a
documentation tool, the class is documented in prose. The clean fix belongs in scan/scan.py and would
make the autodoc entry possible; happy to file that separately if wanted. source_folder and
destination_folder are excluded for the same class of reason — their annotations reference a
ClassVar, not a type.

CONTRIBUTING.md shrinks by 157 lines. Intentional, per #599. Nothing is lost — the content moved
into the Sphinx guide, and the changelog-fragment requirement is repeated in the entry point because it
is a CI gate contributors hit at PR time.

Changelog fragments are included for each closed issue, plus +interface-boundary.doc.md for the
#597 work, which is not named after an issue because it does not close one.

Unrelated observation: nox -s lint runs ruff format in write mode and reformats a line in
noxfile.py that this PR does not touch, meaning master is not currently ruff format-clean. Kept
out of this PR; may deserve its own issue.


Test plan

  • nox -s doc — builds clean. The only warning is a missing local graphviz binary, which is
    pre-existing and environmental; verified by building with and without -W
  • nox -s towncrier_check — all 12 changelog fragments valid
  • nox -s lint — passes
  • Unit suite for v3 — 743 passed, 64 skipped, 0 failed
  • Verified every symbol used in a new example actually exists and is importable
    (NonExistentBucket, interpret_b2_error, update_file_retention, the replication classes)
  • Verified new cross-references resolve in the built HTML, and that the retained todo:: directive
    stays out of the published build but appears under B2_SPHINX_TODO=1

Closes #584
Closes #585
Closes #586
Closes #588
Closes #589
Closes #590
Closes #591
Closes #592
Closes #593
Closes #595
Closes #599
Closes #601

ffumero2003 and others added 12 commits September 9, 2026 15:32
Corrects overalapping, manullay, "Streams that contains" and "If all
candidates fails" in the Advanced usage patterns guide, and replaces the
time-relative "developed for 3 years" claim on the overview page.

Closes Backblaze#591

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Installation Guide told readers to check out the repository and then
run "pip install b2sdk", which resolves the published package from PyPI
and ignores local changes. Use an editable install of the checkout, and
point at the Contributors Guide for the full nox/uv setup.

Closes Backblaze#590

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Sphinx contributor guide advertised a "deploy" nox session that does
not exist; releases are published by the Continuous Delivery workflow on
a version tag. Both contributor guides also claimed a matrix of 8 Python
versions, which no longer matches PYTHON_VERSIONS; describe the matrix by
reference so it does not drift again.

Closes Backblaze#588
Closes Backblaze#589

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The sync example used a nonexistent EncryptionSettings class, passed id=
to EncryptionKey instead of key_id=, and gave
BasicSyncEncryptionSettingsProvider one mapping where it requires both
read and write mappings. Uploads documented file_infos= instead of
file_info=. The file-lock examples also handed a FileRetentionSetting to
update_file_legal_hold, which takes a LegalHold; those calls now use
update_file_retention.

Closes Backblaze#584
Closes Backblaze#585

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The prose referred to LocalUploadSource, which is not exported; the
surrounding examples already use UploadSourceLocalFile. One example also
called planner.create_file, while every other example in the section
calls bucket.create_file.

Closes Backblaze#586

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CONTRIBUTING.md and doc/source/contributing.rst duplicated environment
setup, nox sessions, testing and documentation commands, and had already
drifted apart. Move the guidance that only existed in the Markdown copy
(versioning, changelog fragments, dependency management, keyword-filtered
tests) into the Sphinx guide, and reduce CONTRIBUTING.md to a short
GitHub-facing entry point that links to it.

Closes Backblaze#599

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The repository keeps four numbered interfaces working at once, and the
unit suite is what enforces that, but nothing explained how the apiver
machinery selects tests. A test could therefore be placed where it covers
only one interface even though the changed _internal behaviour is shared.
Add a decision table for the shared, per-version, v_all and integration
locations, document the apiver marker, and add a checklist for _internal
changes.

Closes Backblaze#592

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The guide was a single instruction to run make_release_commit and follow
its output, leaving the boundaries undocumented: that the session builds
the changelog but neither commits nor tags, that CI must be green before
tagging because a PyPI version cannot be reused, and that CD creates a
GitHub release but publishes to PyPI only when the token is present and
the tag is not a prerelease. Document those, plus recovery before and
after a tag is pushed.

Closes Backblaze#593

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The interface policy described which modules are public but said nothing
about the versioned namespace itself, which re-exports far more than the
public interface - low-level HTTP classes, simulators, constants, and
names leaked by star-imports. State that importability is not a promise,
and give the rule for telling public, internal and unclassified names
apart.

The per-name classification that Backblaze#597 also asks for still needs
maintainer review, so this does not close the issue.

Refs Backblaze#597

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replication was fully exposed by the SDK but undocumented, discoverable
only by guessing at names. Add a task-oriented guide covering setup via
ReplicationSetupHelper and by hand, the application-key capabilities each
side needs, per-file replication status, monitoring a rule, and how to
read and interpret a scan report. Add reference pages for the public
replication classes.

Documenting ReplicationMonitor also required indenting the continuation
lines of its docstring's parameter list, which docutils otherwise rejects.
No executable code is changed.

ReplicationReport is described in prose rather than generated, because
its inherited annotations cannot be resolved from the subclass's module.
Fixing that properly belongs in b2sdk/_internal/scan/scan.py.

Closes Backblaze#595

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Five of the six todo directives are addressed: the "delete
doc/source/b2sdk?" note is obsolete since that directory no longer
exists, raw_simulator already has an autodoc page, the "Why use b2sdk?"
list is now consistently phrased and links to the features it mentions,
and the progress reporters and exception hierarchy have real
documentation rather than a note saying they need some.

The remaining directive, in advanced.rst, asks for `prioritize` to accept
an enum instead of a string. That is an API design change rather than
documentation, so it is left in place until an issue exists to link it to.

Refs Backblaze#601

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The last todo directive asked for `prioritize` to accept an enum instead
of a string, which is an API design change rather than documentation
work. It is now tracked as issue Backblaze#602 and the directive links to it,
retained in place so that anyone editing that section sees it next to the
documented string values. It stays out of published builds, since the
todo directive only renders when B2_SPHINX_TODO is set.

Closes Backblaze#601

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment