Skip to content

Instantly share code, notes, and snippets.

@dhellmann
Created November 28, 2025 15:08
Show Gist options
  • Select an option

  • Save dhellmann/ce952573576d030bb44067383230f96a to your computer and use it in GitHub Desktop.

Select an option

Save dhellmann/ce952573576d030bb44067383230f96a to your computer and use it in GitHub Desktop.
Fix for sphinxcontrib-spelling Issue #234 - TypeError when source is None in Sphinx 8.2

Chat Transcript: Fix for sphinxcontrib-spelling Issue #234

Issue Summary

GitHub Issue: sphinx-contrib/spelling#234

Problem: The sphinxcontrib-spelling extension crashes with Sphinx 8.2.3 when docutils.utils.get_source_line(node) returns None for the source parameter.

Error:

TypeError: expected str, bytes or os.PathLike object, not NoneType

Location: sphinxcontrib/spelling/builder.py, line 231

Solution Approach

Investigation

  1. Examined the existing test suite in tests/test_builder.py
  2. Analyzed the bug report and traceback
  3. Identified that the issue occurs when source is None and gets passed to osutil.relpath()

Test Case Development

Several approaches were attempted:

  1. Custom directive - Creating nodes without source information
  2. Transform - Clearing source info after parsing
  3. Mocking get_source_line() - ✅ Successfully reproduced the issue

The final approach directly patches docutils.utils.get_source_line() to return (None, lineno), which reliably reproduces the exact condition from issue #234.

Test Implementation

Created test_nodes_with_none_source() in tests/test_builder.py:

  • Creates a mock extension that patches get_source_line()
  • Returns None for source to trigger the bug
  • Verifies that spelling checks still work after the fix

Bug Fix

Modified sphinxcontrib/spelling/builder.py (lines 231-236):

source, node_lineno = docutils.utils.get_source_line(node)
if source is not None:
    source = osutil.relpath(source)
else:
    # Some nodes (e.g., programmatically generated) may not have
    # source information. Use a placeholder.
    source = "<unknown>"

Changes Made

Files Modified

  1. sphinxcontrib/spelling/builder.py

    • Added null check before calling osutil.relpath(source)
    • Use "<unknown>" placeholder when source is None
    • Follows existing pattern (line 239 already handles node_lineno being None)
  2. tests/test_builder.py

    • Added test_nodes_with_none_source() test function
    • Creates mock extension to simulate the bug condition
    • Verifies fix handles None source gracefully

Test Results

  • ✅ New test fails before fix (reproduces issue #234)
  • ✅ New test passes after fix
  • ✅ All 69 tests pass
  • ✅ No new linting errors

Git Workflow

Branch Created

git checkout -b fix-issue-234-none-source

Commit Details

commit ea81be98eba67d102a3f4b6829f36664e5d5b8c7
Author: Doug Hellmann <doug@doughellmann.com>
Date:   Fri Nov 28 10:06:19 2025 -0500

    Fix TypeError when source is None in Sphinx 8.2
    
    Fixes #234
    
    When docutils.utils.get_source_line() returns None for the source
    parameter, the code was attempting to call osutil.relpath(None),
    which caused a TypeError in Sphinx 8.2.3.
    
    This can occur with programmatically generated nodes or certain
    document structures that don't have proper source attribution.
    
    Changes:
    - Added null check before calling osutil.relpath() in builder.py
    - Use '<unknown>' placeholder when source is None
    - Added test case that reproduces the issue and verifies the fix
    
    Co-authored-by: Claude Sonnet 4.5 <ai-assistant@anthropic.com>

 sphinxcontrib/spelling/builder.py |  7 +++-
 tests/test_builder.py             | 74 +++++++++++++++++++++++++++
 2 files changed, 80 insertions(+), 1 deletion(-)

Key Learnings

  1. Testing Strategy: Direct mocking of the problematic function was more reliable than trying to create document structures that naturally trigger the bug.

  2. Test Design: The test initially used pytest.raises(TypeError) which made it pass when the bug was present. This was corrected to fail when the bug exists and pass when fixed.

  3. Defensive Programming: The fix mirrors the existing pattern in the code where node_lineno is already checked for None before use.

  4. Compatibility: This issue appears in Sphinx 8.2+ when certain types of nodes (programmatically generated content, synthetic nodes) don't have proper source attribution.

Commands Used

# Run specific test
hatch run test:python -m pytest tests/test_builder.py::test_nodes_with_none_source -v

# Run all builder tests
hatch run test:python -m pytest tests/test_builder.py -v

# Run full test suite
hatch run test:python -m pytest tests/ -v

# Create branch and commit
git checkout -b fix-issue-234-none-source
git add sphinxcontrib/spelling/builder.py tests/test_builder.py
git commit -m "..."

Attribution

This fix was developed through pair programming with Claude Sonnet 4.5 as indicated in the commit co-author attribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment