maaz.code logo
Simple view

Mergednumpy/numpy#3285533.3Kstars

DOC,TST: Fix code snippets in subclassing ndarray (User Guide docs) and stop xfailing it in tutorial doctests

By Mohd Maaz Khan, merged by mattip into numpy/numpy on 6 Oct 2026

+164−202164 lines added, 202 lines removed2 files changed8 comments

Conversation

  1. maaz7409opened this pull request on 2 Oct 2026description.mdAuthor

    PR summary

    Currently, line 639 of doc/source/user/basics.subclassing.rst

    super().__array_wrap__(self, out_arr, context, return_scalar)

    explicitly passes self parameter.

    When code is ran according to doc snippet, it raises TypeError: takes at most 3 arguments (4 given) as shown below

    >>> obj = MySubClass(np.arange(5), info='spam')
    In __array_finalize__:
       self is MySubClass([0, 1, 2, 3, 4])
       obj is array([0, 1, 2, 3, 4])
    >>> arr2 = np.arange(5)+1
    >>> ret = np.add(arr2,obj)
    In __array_wrap__:
       self is MySubClass([0, 1, 2, 3, 4])
       arr is array([1, 3, 5, 7, 9])
    In __array_wrap__:
       self is MySubClass([0, 1, 2, 3, 4])
       arr is array([1, 3, 5, 7, 9])
    In __array_wrap__:
       self is MySubClass([0, 1, 2, 3, 4])
       arr is array([1, 3, 5, 7, 9])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/workspaces/numpy/doc/bug-reports/tests/test1.py", line 22, in __array_wrap__
        return super().__array_wrap__(self, out_arr, context, return_scalar)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    TypeError: __array_wrap__() takes at most 3 arguments (4 given)
    

    This PR fixed it by simply removing self being passed as 1st argument, because when calling parent class method super(), self shouldn't be passed explicitly.

    super().__array_wrap__(out_arr, context, return_scalar)

    I've tested this and it works.

    >>> obj = MySubClass(np.arange(5), info='spam')
    In __array_finalize__:
       self is MySubClass([0, 1, 2, 3, 4])
       obj is array([0, 1, 2, 3, 4])
    >>> arr2 = np.arange(5)+1
    >>> ret = np.add(arr2,obj)
    In __array_wrap__:
       self is MySubClass([0, 1, 2, 3, 4])
       arr is array([1, 3, 5, 7, 9])
    In __array_finalize__:
       self is MySubClass([1, 3, 5, 7, 9])
       obj is MySubClass([0, 1, 2, 3, 4])
    >>> ret
    MySubClass([1, 3, 5, 7, 9])
    >>> ret.info
    'spam'
    

    AI Disclosure

    Claude Code was used in identifying this bug.

  2. maaz7409commented on 4 Oct 2026Author

    @mattip i looked into it.

    So, answer to your question is no, refguide_check.py can't catch this. It validates the ReST syntax of docstrings and checks that everything in a module's __all__ appears in the reference guide. It doesn't read doc/source/user/*.rst, and it doesn't execute any example code. I ran it locally and it passes, because it never opens this page.

    It used to have --doctests and --rst modes, until the doctesting was moved to spin check-docs and spin check-tutorials in mid-2024. The CI step is also still labelled "check doctests."

    The check that does cover this page is spin check-tutorials. But basics.subclassing.rst is in dt_config.pytest_extra_xfail in numpy/conftest.py, with the reason .. testcode:: admonitions not understood. As far as I can tell, the pytest doctest runner ignores .. testcode:: blocks, so the classes defined there never exist and the page fails with a NameError before reaching this example. When I ran it, the old and the fixed page both showed XFAIL, so the check can't tell them apart.

    From the git historyy , the page also seems to have been excluded from doctesting for a long time (a skip list in refguide_check.py from 2020, then the xfail), so I don't think any CI configuration has run this example since it became an .rst file. ( I'm going by the config at each commit, not old CI logs ).

    The tool that does run .. testcode:: is sphinx-build -b doctest. Running it on this page reproduces the same TypeError on main and doesn't hit it with this PR. I couldn't find anywhere CI runs that builder.

  3. mattipcommented on 5 Oct 2026Member

    So I think this file should be refactored. Instead of an admonition, all the testcode blocks should be reformatted like this block

    >>> class C:
    ...     def __new__(cls, *args):
    ...         print('Cls in __new__:', cls)
    ...         print('Args in __new__:', args)
    ...         # The `object` type __new__ method takes a single argument.
    ...         return object.__new__(cls)
    ...     def __init__(self, *args):
    ...         print('type(self) in __init__:', type(self))
    ...         print('Args in __init__:', args)
    

    so that CI's spin check-tutorials can be run on it.

  4. maaz7409commented on 5 Oct 2026Author

    @mattip I have refactored the page like you suggested :

    • All the .. testcode:: blocks that define classes are now normal >>> / ... blocks, same style as the class C: block you pointed to. There were 7 of them (D, C, InfoArray, RealisticInfoArray, A, MySubClass, SillySubClass).

    • I didn't put blank lines inside the classes. A blank line in the middle of a class breaks copy-paste into a plain Python prompt (the class would end there), and the class C: block doesn't have any either.

    • The three def sum(...) examples at the end are not real code, just method signatures with ... as the body. So instead of >>> I turned them into .. code-block:: python. Their ... body was also not indented under the def, which isn't valid Python, so I fixed the indentation too.

    • I didn't change any of the actual example code. Only the expected output.

    Once the examples actually ran, 10 of them failed, and in every case the code was fine and only the printed output in the docs was out of date:

    • Classes defined in the page print with the module name, e.g. <class '__main__.C'> and not <class 'C'>. The page already used the __main__. form near the top, but not further down (D, C, InfoArray, RealisticInfoArray). So 8 examples needed that.
    • Two lines still had the old Python 2 style output: <type 'NoneType'> and <type 'numpy.ndarray'>. Now they're <class 'NoneType'> and <class 'numpy.ndarray'>.

    In a separate commit I removed basics.subclassing.rst from dt_config.pytest_extra_xfail in numpy/conftest.py. The page passes now, and if it stays on the xfail list, a future break would just show up as XFAIL and CI would stay green, which is how this bug got through.

    We can add the TST label to PR title since it also changes test configuration.

    A title something like this

    DOC,TST: Fix code snippets in subclassing ndarray ( User guide docs) and drop xfail for it
    

    Is this alright?

    Tests :

    1. spin check-tutorials doc/source/user/basics.subclassing.rst → passes. Before the change it was XFAIL.

    IMG_20261005_203651

    1. To be sure the test really catches the original bug, I put the extra self back in super().__array_wrap__(...) for a moment. The check then failed with TypeError: __array_wrap__() takes at most 3 arguments (4 given), so it would have caught this.

    IMG_20261005_203616

    1. built the docs locally via SPHINXOPTS="-W -n" spin docs

    Screenshot_20261005-205309

    Should I make the push? Or do I first paste the diff docstring ( showing the changes I made)?

  5. mattipcommented on 5 Oct 2026Member

    Commit the changes and push to your branch. Then this PR will have two commits. Documentation CI will run, confirming the chsnges work as advertised.

    Thanks

  6. maaz7409commented on 6 Oct 2026Author

    Looks like Actions had some issue IMG_20261006_022627

  7. mattipcommented on 6 Oct 2026Member

    Actions was flaky yesterday, but the CI build ran and the important line shows success:

    doc/source/user/basics.subclassing.rst::basics.subclassing.rst PASSED [ 44%]

  8. mattipmerged this pull request on 6 Oct 2026

Copied from GitHub when this site was built; comments by bots are left out.