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

Fix Python domain nesting #3520

Merged
merged 2 commits into from
Mar 18, 2017
Merged

Conversation

agjohnson
Copy link
Contributor

Moved #3465 here, to address this in stable instead.

This fixes a problem with the Python domain object nesting. Because only
one object name was stored in ref_context, and reset to None in
after_content, nesting broke if you put anything after a nested class:

.. py:class:: Parent

    .. py:method:: foo()

        This wouldn't resolve: :py:meth:`bar`

    .. py:class:: Child

        In the `after_content` method, the object is reset to `None`, so
       anything after this in the same nesting is considered to be top level
       instead.

    .. py:method:: bar()

        This is top level, as the domain thinks the surrounding object is `None`

This depends on #3519 and can be rebased after that is merged into stable

Fixes #3065 Refs #3067

Moved sphinx-doc#3465 here, to address this in `stable` instead.

This fixes a problem with the Python domain object nesting. Because only one
object name was stored in `ref_context`, and reset to `None` in `after_content`,
nesting broke if you put anything after a nested class:

```rst
.. py:class:: Parent

    .. py:method:: foo()

        This wouldn't resolve: :py:meth:`bar`

    .. py:class:: Child

        In the `after_content` method, the object is reset to `None`, so
        anything after this in the same nesting is considered to be top level
        instead.

    .. py:method:: bar()

        This is top level, as the domain thinks the surrounding object is `None`
```

This depends on sphinx-doc#3519 and can be rebased after that is merged into stable

Fixes sphinx-doc#3065
Refs sphinx-doc#3067
@agjohnson
Copy link
Contributor Author

Rebased on stable with #3519 merged

Copy link
Member

@tk0miya tk0miya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost LGTM

try:
self.env.ref_context['py:classes'].append(prefix)
except (AttributeError, KeyError):
self.env.ref_context['py:classes'] = [prefix]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use dict.setdefault() instead.
Then you don't need to catch any exceptions:

classes = self.env.ref_context.setdefault('py:classes', [])
classes.append(prefix)

if self.allow_nesting:
try:
self.env.ref_context['py:classes'].pop()
except (KeyError, IndexError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these exception raised here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both can, yes. KeyError if py:classes has not been set yet, and IndexError if this is an empty list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry. I understand your code now.
Surely, it will be raised in case of no prefix found in before_content().
Okay, no problem.

except IndexError:
cls_name = None
finally:
self.env.ref_context['py:class'] = cls_name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to simple implementation. How about this?

if len(self.env.ref_context['py:classes']) > 0:
    self.env.ref_context['py:class'] = self.env.ref_context['py:classes'][-1]
else:
    self.env.ref_context['py:class'] = None

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original is more succinct and pythonic, but I don't have a strong opinion. This change looks fine

"""
prefix = None
if self.names:
(cls_name, cls_name_prefix) = self.names.pop()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until now, self.names.pop() is not called.
Is there any side effect?

(cls_name, cls_name_prefix) = self.names.pop()
prefix = cls_name_prefix.strip('.') if cls_name_prefix else None
if self.allow_nesting:
prefix = cls_name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefix is determined by both allow_nesting and cls_name_prefix.
So how about this? I feel this is more simple.

if self.allow_nesting:
    prefix = cls_name
elif cls_name_prefix:
    prefix = cls_name_prefix.strip('.')
else:
    prefix = None

# type: () -> None
"""Handle object nesting before content

If this class is a nestable object, such as a class object, build up a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel a bit strange for "this class". How about "this instance" or "the instance"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like class is the most correct, as allow_nesting is used as a class variable. I can find a clearer way of describing this though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I felt confused the description uses "class" twice. The first class; "this class" means subclasses of PyObject. It is an implementation of directives. so my proposal was wrong.
And second one; "a class object" means subjects of directives. Sometime it is a class, a method, and so on.

I'm not a native English speaker and not good at English. so If you feel this is okay, go ahead with this.

"""
prefix = None
if self.names:
(cls_name, cls_name_prefix) = self.names.pop()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... self.names.pop() contains always "class name" ?
As a quick look, it also contains modules, methods, functions and so on.
If my consideration is true, please rename this to better one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I'll update this

@tk0miya tk0miya added this to the 1.5.4 milestone Mar 7, 2017
@agjohnson
Copy link
Contributor Author

Things are now cleaned up and rebased

@tk0miya tk0miya merged commit a67c785 into sphinx-doc:stable Mar 18, 2017
@tk0miya
Copy link
Member

tk0miya commented Mar 18, 2017

Merged. Thank you for contribution!

tk0miya added a commit that referenced this pull request Mar 18, 2017
@agjohnson agjohnson deleted the fix-domain-py-nesting branch March 20, 2017 19:01
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants