Skip to content

🐛 sphinx-needs: needs_functions shapes that crashed the build now warn - #2112

Merged
chrisjsewell merged 7 commits into
masterfrom
claude/magical-hopper-uw9j1q
Oct 7, 2026
Merged

chrisjsewell merged 7 commits into
masterfrom
claude/magical-hopper-uw9j1q

Conversation

@chrisjsewell

@chrisjsewell chrisjsewell commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

What

Package: packages/sphinx-needs.

Follow-up to #2079, which made a non-callable needs_functions entry a needs.config warning.
Three neighbouring shapes still ended the build during config-inited, before a single document was read:

conf.py on master
needs_functions = None TypeError: 'NoneType' object is not iterable (from merge_default_configs)
needs_functions = my_function TypeError: 'function' object is not iterable
needs_functions = [functools.partial(f, x=1)], or an instance with __call__ AttributeError: 'functools.partial' object has no attribute '__name__' (from add_function)

A string, needs_functions = "my_function", did not crash but was read one character at a time: one
needs_functions entry 'm' is not callable and is ignored warning per character.

Each is now reported and the build continues:

  • A value that is not a list (or tuple) is ONE warning naming its type, and nothing is registered from it (the built-in functions still are):

    WARNING: needs_functions is of type 'NoneType', not a list of callables, and is ignored [needs.config]
    WARNING: needs_functions is of type 'function', not a list of callables, and is ignored [needs.config]
    WARNING: needs_functions is of type 'str', not a list of callables, and is ignored [needs.config]
    

    Sphinx's own type check (check_confval_types, config-inited priority 800) would report the same value again
    (The config value `needs_functions' has type `NoneType'; expected `list'., measured), so after warning the value is
    replaced with the confval's default []; it is reported once, under needs.config like its neighbours.
    The declared types=(list,) is unchanged.
    A set of functions, or another iterable of functions that is neither a list nor a tuple (a frozenset, a generator), used to register its functions beside
    Sphinx's own type warning, and is now ignored with the same needs.config warning.
    A tuple is still registered, as before (Sphinx's own type warning for it is unchanged).

  • An entry that is callable but has no __name__ is skipped with one warning, and the rest of the list is still registered:

    WARNING: needs_functions entry functools.partial(<function echo_word at 0x...>, word='forty-two') has no __name__ and is ignored: an entry must be a callable with a __name__; use add_dynamic_function(app, func, name=...) for one without [needs.config]
    
  • A non-callable entry keeps 🐛 sphinx-needs: a TOML functions key warns instead of crashing #2079's warning, unchanged.

  • The API path, add_dynamic_function(app, func) without name= for a callable without a __name__, raised a bare
    AttributeError. It now raises NeedsApiConfigException (as add_warning does for a missing argument):
    Dynamic function functools.partial(...) has no __name__; pass the name to call it by with name=....
    The rule is one sentence in the add_dynamic_function docstring, and in the Registration section of dynamic_functions.rst.

The changelog bullet is marked (changed output): every changed shape crashed or already warned, but a set-valued
needs_functions ({my_function}), which Sphinx already reported as the wrong type, had its functions registered and their
[[my_function()]] calls resolved; it is now ignored, so those calls report Unknown function 'my_function'.

Why

Issue #2073, the follow-up of #2064's second item: needs_functions values and entries that end the build with a traceback
instead of a configuration warning, so a mistake in conf.py is reported like the others and the build continues.

Closes #2073

Tests

All in tests/test_needs_from_toml_functions.py, the suite #2079 wrote for needs_functions; each build test is a real in-process build
asserting the whole build_warnings(app) list and that the build completes. The tests for the crashing shapes were committed first
and were red against master; the tuple test guards what master already did.

test pins
test_a_needs_functions_value_that_is_not_a_list_warns_once_and_is_ignored[none] None: one warning, no Sphinx duplicate, needs_functions == [], only the built-ins registered
…[bare-function] a bare function: the same
…[string] a string: one warning instead of one per character
…[set] a set: one warning, nothing registered from it (it used to register, beside Sphinx's type warning)
test_a_needs_functions_entry_without_a_name_warns_and_is_ignored[partial] a functools.partial is skipped with one warning; the next entry is registered and resolves in a need
…[instance] an instance with __call__: the same
test_a_needs_functions_tuple_is_still_registered a tuple still registers and resolves; the only warning is Sphinx's own has type `tuple'; expected `list'.
test_add_dynamic_function_asks_for_a_name_for_a_callable_without_one the API raises NeedsApiConfigException asking for name=, registers nothing; with name= it registers

Mutation checks (each applied, the file run, reverted):

mutation red
remove the not-a-list guard [none], [bare-function] (crash), [string] (per-character list), [set] (registered beside Sphinx's warning)
narrow the guard to list only the tuple test (its function becomes Unknown function 'answer')
remove the no-__name__ guard [partial], [instance] (crash)
skip a nameless entry without warning [partial], [instance] (assert [] == [...])
add_function reads function.__name__ again the API test (AttributeError)

uv run poe lint and uv run poe typecheck pass. The full suite passes apart from the graphviz-rendering tests, which need dot and fail
identically without this change on a machine that lacks it.

Checklist

  • I wrote this change myself and have read every line of it; it was not generated automatically from an issue.
    Built, adversarially reviewed and validated in an orchestrated Claude Code session from the issue, the code and the
    tests of 🐛 sphinx-needs: a TOML functions key warns instead of crashing #2079; every line was read by its builder, its reviewer and the orchestrator, and the tests were committed
    first and shown red against master.
  • I ran the package's tests (uv run poe test-needs) and they pass (the graphviz renders excepted on a machine
    without dot, identically to master).
  • Documentation is updated where behaviour or options change (docs/dynamic_functions.rst, the Registration section).
  • The package's docs/changelog.rst has an entry under Unreleased.
  • uv run poe lint and uv run poe typecheck pass.

ubCode

Nothing to match in ubCode, because ubCode registers no Python callables: needs_functions is a conf.py value it never reads
(docs/source/configuration/needs.rst:1684: "Functions a project registers through needs_functions are not resolved"), and a
functions key under [needs] in ubproject.toml is dropped silently, because NeedsConfig carries #[serde(default)] without
deny_unknown_fields (rust/ubc_config/src/needs/input.rs:23-25); reporting unknown keys is the open useblocks/ubcode#2931.
The shapes fixed here are Python-object shapes (a partial, None, a bare function) that cannot be written in TOML.
(Measured at ubCode origin/main, 8bece77.)

Follow-ups

  • A tuple is accepted by sphinx-needs, but Sphinx's own check still warns that it is not a list
    (The config value `needs_functions' has type `tuple'; expected `list'., unchanged from master).
    Declaring types=(list, tuple) would be a separate, deliberate change to the confval.

When squashing, strip the Co-authored-by trailer GitHub proposes.

@github-actions github-actions Bot added the pkg: sphinx-needs The sphinx-needs distribution (packages/sphinx-needs): its code, tests and docs label Oct 7, 2026
@chrisjsewell chrisjsewell added the bug label Oct 7, 2026 — with Claude
@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.49%. Comparing base (9fb1563) to head (29bab8a).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2112      +/-   ##
==========================================
+ Coverage   92.48%   92.49%   +0.01%     
==========================================
  Files         136      136              
  Lines       19416    19429      +13     
==========================================
+ Hits        17957    17971      +14     
+ Misses       1459     1458       -1     
Flag Coverage Δ
ai-index 100.00% <ø> (ø)
codelinks 95.35% <ø> (ø)
mounts 94.27% <ø> (ø)
pytests 91.91% <100.00%> (+0.01%) ⬆️
reports 88.01% <ø> (ø)
ub-test-reports 90.16% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chrisjsewell
chrisjsewell merged commit ca0d997 into master Oct 7, 2026
42 checks passed
@chrisjsewell
chrisjsewell deleted the claude/magical-hopper-uw9j1q branch October 7, 2026 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug pkg: sphinx-needs The sphinx-needs distribution (packages/sphinx-needs): its code, tests and docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

needs_functions entries that are callable without a __name__, or a non-iterable value, still crash the build

2 participants