-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Use tags in docs building to split up requirements #13168
base: main
Are you sure you want to change the base?
Use tags in docs building to split up requirements #13168
Conversation
🤔 I need to tell readthedocs about EDIT: quick hack e06b1cf, I don't see any out of the box support for them (I found one issue: readthedocs/readthedocs.org#4603) |
docs/html/conf.py
Outdated
] | ||
|
||
# 'tags' is a special object handled by sphinx | ||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-tags | ||
if "html" in tags or "READTHEDOCS" in os.environ: # type: ignore[name-defined] # noqa: F821 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The linters get a bit upset with using tags
like this, and complain about each nice, but maybe more usage makes things more clear, e.g.:
_tags: Tags = tags # type: ignore[name-defined] # noqa: F821
if "READTHEDOCS" in os.environ:
_tags.add("html")
if "html" in _tags:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I've also seen (and commented on) some tag use by @AA-Turner that was accessing them through the Sphinx application object in setup()
. Adam — do you remember of such an example? I can't seem to find a project that uses said approach…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found it! I was thinking about those projects linked in sphinx-doc/sphinx#12490. Looks like the tags are accessible via app.tags.tags
in the extension context...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The object is injected into conf.py
. Please only use tags
or app.tags
, we have deprecated app.tags.tags
!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I know it's injected. I was just thinking about the postponed evaluation case. So I'd go for calling app.setup_extension()
based on the state of app.tags
or the currently selected builder…
@eli-schwartz would gating the HTML build behind a Sphinx tag |
This is a neat trick that I didn't know sphinx could do!
We can, yes. It's already necessary in order to pass That being said, it looks like this actually defaults to omitting the html logic by default (it doesn't matter whether you use |
docs/html/conf.py
Outdated
] | ||
|
||
# 'tags' is a special object handled by sphinx | ||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-tags | ||
if "html" in tags or "READTHEDOCS" in os.environ: # type: ignore[name-defined] # noqa: F821 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I think the idiomatic check is tags.has('html')
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that it's not just html
but htmldir
too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I think the idiomatic check is
tags.has('html')
.
I was just following the docs on that one https://www.sphinx-doc.org/en/master/usage/configuration.html#project-tags (look to be a recent update <1 year ago sphinx-doc/sphinx#12490)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, makes sense.
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-tags | ||
if "html" in tags or "READTHEDOCS" in os.environ: # type: ignore[name-defined] # noqa: F821 | ||
# html specific deps | ||
extensions.extend( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It occurred to me that maybe if the extension loading was to be moved to the setup()
entry point, the builder name would already be known by then and so this might not even have to use tags but would rely on the requested builders.
Another approach I used in the past, in a little different context, was scanning for the builder args in sys.argv
: sphinx-contrib/spelling#55 (comment) / cherrypy/cheroot@63097fb#diff-85933aa74a2d66c3e4dcdf7a9ad8397f5a7971080d34ef1108296a7c6b69e7e3R12-R15.
That's one of the reasons I've migrated my RTD configs to just call the project's main workflow tool: https://github.com/ansible/awx-plugins/blob/0d569b5/.readthedocs.yaml#L27-L29. In my case it's tox, but in pip's case it'd be nox. This brings RTD and local+CI builds even closer in what they produce. |
docs/html/conf.py
Outdated
"sphinx.ext.extlinks", | ||
"sphinx.ext.intersphinx", | ||
# our extensions | ||
# extensions common to 'man' and 'html' builds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also recommend keeping the order of extensions: first-party -> third-party -> local. Just like with regular imports.
docs/html/conf.py
Outdated
# first-party extensions | ||
"sphinx.ext.autodoc", | ||
"sphinx.ext.todo", | ||
"sphinx.ext.extlinks", | ||
"sphinx.ext.intersphinx", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are all built-in so there's no value moving to conditional (and it makes things harder to reason about)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there might be value in skipping some code paths, though.
"sphinx_copybutton", | ||
"sphinx_inline_tabs", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two seem to be the only genuine 'HTML-only' extensions. Perhaps the condition might be better expressed as 'man' not in tags
?
You could also look at the Python docs, where we use a pattern to check if an extension is importable, and if so add it to extensions
. Eg
import importlib.util
if importlib.util.find_spec(...):
extensions.append(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps the condition might be better expressed as 'man' not in tags?
👍 this was also suggested above (#13168 (comment)) and I agree it makes this clearer (and simplifies the code!) so trying that: 033c31c
docs/html/conf.py
Outdated
] | ||
|
||
# 'tags' is a special object handled by sphinx | ||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-tags | ||
if "html" in tags or "READTHEDOCS" in os.environ: # type: ignore[name-defined] # noqa: F821 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The object is injected into conf.py
. Please only use tags
or app.tags
, we have deprecated app.tags.tags
!
82da058
to
033c31c
Compare
docs/html/conf.py
Outdated
if "man" not in tags: # type: ignore[name-defined] # noqa: F821 | ||
# extensions not needed for building man pages | ||
extensions.extend( | ||
( | ||
# first-party extensions | ||
"sphinx.ext.autodoc", | ||
"sphinx.ext.todo", | ||
"sphinx.ext.extlinks", | ||
"sphinx.ext.intersphinx", | ||
# third-party extensions | ||
"myst_parser", | ||
"sphinx_copybutton", | ||
"sphinx_inline_tabs", | ||
"sphinxcontrib.towncrier", | ||
), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be cleaner?
if "man" not in tags: # type: ignore[name-defined] # noqa: F821 | |
# extensions not needed for building man pages | |
extensions.extend( | |
( | |
# first-party extensions | |
"sphinx.ext.autodoc", | |
"sphinx.ext.todo", | |
"sphinx.ext.extlinks", | |
"sphinx.ext.intersphinx", | |
# third-party extensions | |
"myst_parser", | |
"sphinx_copybutton", | |
"sphinx_inline_tabs", | |
"sphinxcontrib.towncrier", | |
), | |
) | |
IS_MAN_BUILD = "man" in tags # type: ignore[name-defined] # noqa: F821 | |
extensions += [] if IS_MAN_BUILD else [ | |
# first-party extensions | |
"sphinx.ext.autodoc", | |
"sphinx.ext.todo", | |
"sphinx.ext.extlinks", | |
"sphinx.ext.intersphinx", | |
# third-party extensions | |
"myst_parser", | |
"sphinx_copybutton", | |
"sphinx_inline_tabs", | |
"sphinxcontrib.towncrier", | |
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! I'll wait for other people to comment, however.
Oh yeah, we should probably add a docs news entry to let our redistributors know about this change. |
The goal is to allow a build of just the `man` pages with a minimal set of dependencies, like: sphinx-build \ --tag man \ -c docs/html \ -d docs/build/doctrees/man \ -b man \ docs/man \ docs/build/man This is for the sake of people distributing `pip` who want to build the man pages (but not the HTML ones) along side the application. This is an alternative to the approach proposed in[1] and similarly addresses[2] Link: pypa#12900 [1] Link: pypa#12881 [2]
10ef204
to
782c3a2
Compare
Done, and squashed my fixup commits in with 782c3a2 There have been some good suggestions on this PR about large restructurings of the |
The goal is to allow a build of just the
man
pages with a minimal set of dependencies, like:This is for the sake of people distributing
pip
who want to build the man pages (but not the HTML ones) along side the application.This is an alternative to the approach proposed in[1] and similarly addresses[2]
Link: #12900 [1]
Link: #12881 [2]