Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] MemoryError in sphinx.ext.intersphinx and undesirable side-effects in domains. #11337

Closed
picnixz opened this issue Apr 18, 2023 · 0 comments · Fixed by #11338
Closed

[BUG] MemoryError in sphinx.ext.intersphinx and undesirable side-effects in domains. #11337

picnixz opened this issue Apr 18, 2023 · 0 comments · Fixed by #11338

Comments

@picnixz
Copy link
Member

picnixz commented Apr 18, 2023

Describe the bug

As mentioned in #11282 (comment), I found that sphinx.ext.intersphinx has a serious bug when parsing a document with the following structure:

.. py:function:: foo(x)
   
   The foo function.
   
   :param x: Some x.
   :type x: typing.Any

.. py:function:: bar(a)
   
   The bar function.
   
   :param a: Some a.
   :type a: typing.Any

As I said, here what happens during the resolution phase:

if type is None:
objtypes = list(self.object_types)
else:
objtypes = self.objtypes_for_role(type)

In particular, when encountering typing.* or None in an inline reference such as :type NAME: TYPE, the latter will be considered as an obj reference (and not a class reference). This is due to

if reftarget == 'None' or reftarget.startswith('typing.'):
# typing module provides non-class types. Obj reference is good to refer them.
reftype = 'obj'
else:
reftype = 'class'

Now, if sphinx.ext.intersphinx is enabled, we have:

def _resolve_reference_in_domain(env: BuildEnvironment,
inv_name: str | None, inventory: Inventory,
honor_disabled_refs: bool,
domain: Domain, objtypes: list[str],
node: pending_xref, contnode: TextElement,
) -> nodes.reference | None:
# we adjust the object types for backwards compatibility
if domain.name == 'std' and 'cmdoption' in objtypes:
# until Sphinx-1.6, cmdoptions are stored as std:option
objtypes.append('option')
if domain.name == 'py' and 'attribute' in objtypes:
# Since Sphinx-2.1, properties are stored as py:method
objtypes.append('method')
# the inventory contains domain:type as objtype
objtypes = [f"{domain.name}:{t}" for t in objtypes]

I found the following issues:

  • If the role is py:obj, the corresponding object types are all possible object types. In particular, 'attribute' and 'method' coexist and 'method' will be duplicated. This can lead to a MemoryError (I had a medium-size project where object_types grow more than of a hundred times so I can imagine when there are VERY large projects).
  • In
    domain = env.get_domain(domain_name)
    objtypes = domain.objtypes_for_role(typ)
    if not objtypes:
    return None
    return _resolve_reference_in_domain(env, inv_name, inventory,
    honor_disabled_refs,
    domain, objtypes,
    node, contnode)
    the objtypes list is domain._role2type[typ]. This means that any modification to the former propagate to the latter, thereby causing an undesirable side-effect. Note that this currently seems to only affect the py:obj role but the side-effect can also be seen for other property-like roles.

With my above example, the domain's role dispatcher map grows as follows:

# domain._role2type['obj'] when parsing foo()
['function', 'data', 'class', 'exception', 'method', 'classmethod', 'staticmethod', 'attribute', 'property', 'module']

# domain._role2type['obj'] when parsing bar()
['function', 'data', 'class', 'exception', 'method', 'classmethod', 'staticmethod', 'attribute', 'property', 'module', 'method']

How to Reproduce

conf.py

extensions = ['sphinx.ext.intersphinx']
intersphinx_mapping = {'python': ('https://docs.python.org/3',)}

index.rst

.. py:function:: foo(x)
   
   The foo function.
   
   :param x: Some x.
   :type x: typing.Any

.. py:function:: bar(a)
   
   The bar function.
   
   :param a: Some a.
   :type a: typing.Any

Environment Information

Platform:              linux; (Linux-5.3.18-lp152.106-default-x86_64-with-glibc2.26)
Python version:        3.10.3 (main, Jan 31 2023, 10:47:25) [GCC 7.5.0])
Python implementation: CPython
Sphinx version:        6.2.0+/b04fe6f89
Docutils version:      0.18.1
Jinja2 version:        3.1.2
Pygments version:      2.15.0

Sphinx extensions

['sphinx.ext.intersphinx']

Additional context

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
2 participants