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 the URLs of SVG inheritance diagrams #152

Closed
wants to merge 1 commit into from

Conversation

ayshih
Copy link
Contributor

@ayshih ayshih commented Jun 17, 2022

Fixes #48

Currently, SVG inheritance diagrams do not link classes correctly because sphinx.ext.inheritance_diagram prepends the URLs with '../' for SVG output, which conflicts with when the SVG is embedded in a page. See #48 (comment) and linked discussions therein. The '../' gets prepended here, right before the URLs are passed to InheritanceGraph.generate_dot() here.

This PR defines a subclass of InheritanceGraph (AutomodGraph) that overrides generate_dot() to removes the prepended '../' before generating the inheritance diagram. When Automoddiagram uses InheritanceDiagram to create the document node, it casts the contained InheritanceGraph to AutomodGraph so that the overridden generate_dot() will be used in sphinx.ext.inhertiance_diagram code.

@ayshih
Copy link
Contributor Author

ayshih commented Jun 17, 2022

I have this PR as "draft" because I haven't yet tried to write a unit test. It works as intended when I build the SunPy docs.

@pllim
Copy link
Member

pllim commented Jun 17, 2022

Maybe wanna address this warning too:

WARNING: Invalid configuration value found: 'language = None'.
Update your configuration to a valid langauge code. Falling back to 'en' (English).

Would be nice to have another draft PR out to astropy using this branch as proof-of-concept (what works for Sunpy might not work for astropy, you never know).

@codecov
Copy link

codecov bot commented Jun 17, 2022

Codecov Report

Merging #152 (83fdcd2) into main (b602fdc) will decrease coverage by 0.00%.
The diff coverage is 90.90%.

@@            Coverage Diff             @@
##             main     #152      +/-   ##
==========================================
- Coverage   89.43%   89.43%   -0.01%     
==========================================
  Files          23       23              
  Lines        1098     1107       +9     
==========================================
+ Hits          982      990       +8     
- Misses        116      117       +1     
Impacted Files Coverage Δ
sphinx_automodapi/automodsumm.py 85.71% <90.90%> (+0.08%) ⬆️

📣 Codecov can now indicate which changes are the most critical in Pull Requests. Learn more

@ConorMacBride
Copy link
Member

I compared some URLs from the sunpy test build on RTD sunpy/sunpy#6267. It looks like the URL is being resolved relative to the SVG file and not relative to the HTML page where the SVG file is placed. So, assuming images are only ever placed in the _images directory at the root of the built HTML, the href in the SVG files should always and only start with one ../.

RTD latest build

coordinates diagram

Diagram on HTML page:

https://docs.sunpy.org/en/latest/code_ref/coordinates/index.html#class-inheritance-diagram

SVG file:

https://docs.sunpy.org/en/latest/_images/inheritance-ca7d6bcf2d9d402ecb912d0d592f4df3f703031e.svg

href in SVG file:

../../../generated/api/sunpy.coordinates.HeliographicStonyhurst.html#sunpy.coordinates.HeliographicStonyhurst

Resolves to:

https://docs.sunpy.org/generated/api/sunpy.coordinates.HeliographicStonyhurst.html#sunpy.coordinates.HeliographicStonyhurst

map diagram

Diagram on HTML page:

https://docs.sunpy.org/en/latest/code_ref/map.html#class-inheritance-diagram

SVG file:

https://docs.sunpy.org/en/latest/_images/inheritance-96c5441fab156f5bc4605ca615e0948371053c34.svg

href in SVG file:

../../generated/api/sunpy.map.GenericMap.html#sunpy.map.GenericMap

Resolves to:

https://docs.sunpy.org/en/generated/api/sunpy.map.GenericMap.html#sunpy.map.GenericMap

RTD build for sunpy/sunpy#6267

coordinates diagram

Diagram on HTML page:

https://sunpy--6267.org.readthedocs.build/en/6267/code_ref/coordinates/index.html#class-inheritance-diagram

SVG file:

https://sunpy--6267.org.readthedocs.build/en/6267/_images/inheritance-542b0005c1d699e00278de02d0a646342d6ede14.svg

href in SVG file:

../../generated/api/sunpy.coordinates.HeliographicStonyhurst.html#sunpy.coordinates.HeliographicStonyhurst

Resolves to:

https://sunpy--6267.org.readthedocs.build/en/generated/api/sunpy.coordinates.HeliographicStonyhurst.html#sunpy.coordinates.HeliographicStonyhurst

map diagram

Diagram on HTML page:

https://sunpy--6267.org.readthedocs.build/en/6267/code_ref/map.html#class-inheritance-diagram

SVG file:

https://sunpy--6267.org.readthedocs.build/en/6267/_images/inheritance-c734b746a4dcbbe15047e3f5d49973a4ed216e2f.svg

href in SVG file:

../generated/api/sunpy.map.GenericMap.html#sunpy.map.GenericMap

Resolves to:

https://sunpy--6267.org.readthedocs.build/en/6267/generated/api/sunpy.map.GenericMap.html#sunpy.map.GenericMap

@ayshih
Copy link
Contributor Author

ayshih commented Jun 17, 2022

As discussed on Element, the root bug appears to actually be in sphinx, so I'll need to figure out how to fix it (and convince them). This PR is not the solution.

if env is not None:
graphviz_output_format = env.config.graphviz_output_format.upper()
if graphviz_output_format == 'SVG':
urls = {k: v[3:] for k, v in urls.items()}
Copy link
Member

Choose a reason for hiding this comment

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

The suggestion below works for me if I build the sunpy docs and test on a local web server. This way all the hrefs in SVG files only start with ../generated. If I understand the upstream issue correctly, the URLs for the PNG diagram need to be relative to the HTML file while the URLs inside the SVG diagram need to be relative to the SVG file itself? So in this line of the sphinx codebase, they should be able to apply the same fix as here (i.e., remove all "../" from child.get('refuri'))?

Suggested change
urls = {k: v[3:] for k, v in urls.items()}
urls = {k: "../" + v.split("../")[-1] for k, v in urls.items()}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This approach would work fine for the default configuration, but wouldn't be a general solution for all possible configurations of directory structure.

Copy link
Member

Choose a reason for hiding this comment

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

Not sure I understand how a different directory structure could make this not work? (Unless people can change the default image directory.) Can we can assume that child.get('refuri') will always be rooted at the directory where the docs get generated to (yet prepended with any number of ../) and that the SVG files will always be in a directory _images?

I.e., docs are generated to a directory _build/html with files _build/html/user_guide/a/b.html _build/html/api_docs/x/y/z/module.html and a diagram at _build/html/_images/diag.svg.

Therefore child.get('refuri') will be ../../api_docs/x/y/z/module.html#test.class. So child.get('refuri').split("../")[-1] (api_docs/x/y/z/module.html#test.class) is always rooted at the directory containing _images. So, you can then do "../" + child.get('refuri').split("../")[-1] to get the path to module.html relative to diag.svg.

Also, the bug is definitely in sphinx as I was able to recreate it using only sphinx.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My point is that we shouldn't assume that directory structure. The SVG files might get stored in a sub-sub-folder, or the relative path to the API docs might start with a '../'. Even if Sphinx itself doesn't support such a directory structure, it is so extensible that one just doesn't know. All of the information to re-path the URL should be available.

If this were a new bug, a hacky solution would probably be fine to get things working in most cases. Given that this bug is ancient, let's take the extra time to make a proper solution.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, you are correct! I was able to produce a directory structure where it didn't work (as the refuri only goes pack to the first directory in common with the page with the inheritance diagram).

Making the following modifications to ext.inheritance_diagram upstream I can get the links to work reliably on a minimal sphinx doc with lots of different directory hierarchies. I think this should be general solution that will work even if other plugins modify the default paths. What do you think?

404c404,412
<                 urls[child['reftitle']] = "../" + child.get('refuri')
---
>                 from pathlib import Path
>                 current_directory = Path(current_filename).parent
>                 url = "../" + str(
>                     current_directory /
>                     Path(self.builder.imgpath).parent /
>                     current_directory /
>                     Path(child.get('refuri'))
>                 )
>                 urls[child['reftitle']] = url

Copy link
Member

Choose a reason for hiding this comment

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

Hmmm. The imgpath part is redundant in that code so maybe this isn't general...

Copy link
Contributor Author

@ayshih ayshih Jun 18, 2022

Choose a reason for hiding this comment

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

Your patch has some issues. My Sphinx modification is ayshih/sphinx@e711e26, and sunpy/sunpy#6267 shows that it fixes all SunPy inheritance diagrams. Can you try it on your test docs?

Copy link
Member

Choose a reason for hiding this comment

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

I didn't notice that you updated the sunpy PR with a patched version of sphinx rather than this plugin! 🙃 😆 Yeah, your solution is much more robust across different systems etc and with variations in the location of the images! It works perfectly on my test docs with a big tree of directories with diagrams and API docs at all different levels. I think this bug is now finally solved! Well done! 🥳 There's an old issue upstream sphinx-doc/sphinx#3176 (as well as one opened just 8 hours ago! sphinx-doc/sphinx#10570)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ayshih
Copy link
Contributor Author

ayshih commented Jun 18, 2022

I have put up a PR (sphinx-doc/sphinx#10576) that fixes the true bug in Sphinx, so I'm closing this PR

@ayshih ayshih closed this Jun 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sphinx class inheritance diagrams not linking classes correctly
3 participants