Skip to content

DOC: solve_discrete_riccati_system is missing from the API docs, and its deprecation warning points to an import that fails #987

Open

Description

@mmcky

Summary

docs/source/tools/matrix_eqn.rst documents solve_discrete_riccati_system under .. currentmodule:: quantecon, but quantecon/__init__.py does not import that function. Autodoc therefore cannot import it, so the function has no entry in the built API docs, and the build still succeeds. Separately, the deprecated quantecon.matrix_eqn shim tells users to write from quantecon import solve_discrete_riccati_system, which raises ImportError. Adding the name to the existing import in quantecon/__init__.py fixes both problems. Editing the generated .rst by hand would fail CI.

What the code does

docs/source/tools/matrix_eqn.rst#L4-L10:

.. currentmodule:: quantecon
...
.. autofunction:: solve_discrete_riccati_system

quantecon/__init__.py#L45 imports only two of the three functions:

from ._matrix_eqn import solve_discrete_lyapunov, solve_discrete_riccati

The shim's warning, quantecon/matrix_eqn.py#L25-L28:

    warnings.warn(f"Please use `{name}` from the `quantecon` namespace, the "
                  "`quantecon.matrix_eqn` namespace is deprecated. You can use"
                  f" the following instead:\n `from quantecon import {name}`.",
                  category=DeprecationWarning, stacklevel=2)

I built main's docs locally with Sphinx 7.4.7 and numpydoc 1.10.0, using the repo's conf.py with two changes: alabaster in place of sphinx_rtd_theme (not installed here) and intersphinx turned off. The build gives:

WARNING: autodoc: failed to import function 'solve_discrete_riccati_system' from module 'quantecon'; the following exception was raised:
...
AttributeError: module 'quantecon' has no attribute 'solve_discrete_riccati_system'. Did you mean: 'solve_discrete_riccati'?

The built tools/matrix_eqn.html documents only quantecon.solve_discrete_lyapunov and quantecon.solve_discrete_riccati. Sphinx still reports build succeeded, 6 warnings. and exits 0. Nothing flags the missing entry: CI builds without -W (ci.yml#L145-L147), and .readthedocs.yaml sets no fail_on_warning. Read the Docs builds the same committed stub. I have not checked the live page (https://quanteconpy.readthedocs.io/en/latest/tools/matrix_eqn.html), which would confirm this.

The other five warnings are the same failure for five more names: ckron and gridmake (the tools/ce_util page has no entries at all), annotate_nodes, num_compositions_jit and simulate_linear_model. Their shims give the same from quantecon import ... advice, and it fails for each of them.

Reproducer

import warnings

import quantecon as qe
import quantecon.matrix_eqn

print(qe.__version__)
print(hasattr(qe, "solve_discrete_riccati_system"))

# The deprecated shim still works, and says where to import it from instead
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter("always")
    quantecon.matrix_eqn.solve_discrete_riccati_system
print(w[0].category.__name__ + ":", w[0].message)

# Following that advice fails
try:
    from quantecon import solve_discrete_riccati_system
    print("import works")
except ImportError as e:
    print("ImportError:", str(e).split(" (")[0])  # drop the local file path

Output on main (70fa215, Python 3.13.14):

0.13.0
False
DeprecationWarning: Please use `solve_discrete_riccati_system` from the `quantecon` namespace, the `quantecon.matrix_eqn` namespace is deprecated. You can use the following instead:
 `from quantecon import solve_discrete_riccati_system`.
ImportError: cannot import name 'solve_discrete_riccati_system' from 'quantecon'

Suggested fix

Export the function in quantecon/__init__.py#L45:

from ._matrix_eqn import solve_discrete_lyapunov, solve_discrete_riccati, \
    solve_discrete_riccati_system

quantecon has never exported this function, so this adds a public name, which is an API decision for the maintainers. It changes no existing outputs or tests. I checked this on a scratch branch of main with only this change:

  • The reproducer prints True and import works, so the shim's advice becomes correct without changing the shim.
  • The committed stub works unchanged. The docs build documents quantecon.solve_discrete_riccati_system, and the warnings drop to 5 (the five names above).
  • cd docs && python qe_apidoc.py leaves docs/source unchanged, and flake8 --select=F401,F405,E231 quantecon is clean.
  • pytest quantecon collects the same 767 tests as main. All of them pass except the three fetch_nb_dependencies tests in quantecon/util/tests/test_notebooks.py. Those tests download from github.com, which my offline sandbox blocks, and they fail the same way on unmodified main.

Pointing the .rst at quantecon._matrix_eqn by hand does not work, because the files in docs/source are generated. write_base_module in docs/qe_apidoc.py#L275-L281 writes one directive under currentmodule:: quantecon for each name in a shim's __all__. CI reruns the script and fails on any difference (ci.yml#L127-L144). I committed that hand edit on a scratch branch and replayed both CI steps. qe_apidoc.py restored .. autofunction:: solve_discrete_riccati_system, and the drift step failed with ::error::docs/source is stale. make html also reruns qe_apidoc.py first (docs/Makefile#L78-L80).

If the maintainers would rather not add a public name, a docs-only fix has to go in write_base_module. For names that quantecon/__init__.py does not import, it would fall back to quantecon._<module>.<name>; it can find those names with ast, the same way it already reads __all__. The stubs are then regenerated. A scratch prototype rewrote 5 stubs, passed the drift check once they were committed, and built with 0 warnings. Two drawbacks remain: the entry is then titled quantecon._matrix_eqn.solve_discrete_riccati_system, and the shim's advice still fails.

Context

Found while verifying #984, the fix for #982. The stub has used currentmodule:: quantecon since c58ded5 (#850, first released in v0.11.3); the earlier automodule:: quantecon.matrix_eqn stub with :members: documents all three functions when built against main's code.

🤖 Generated with Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions