Skip to content

Commit

Permalink
Merge pull request #8848 from webknjaz/features/8783--sphinx-ext-admo…
Browse files Browse the repository at this point in the history
…nitions
  • Loading branch information
pradyunsg authored Sep 30, 2020
2 parents ccc84a6 + 13962cd commit fe2075b
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 3 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ exclude noxfile.py

recursive-include src/pip/_vendor *.pem
recursive-include src/pip/_vendor py.typed
recursive-include docs Makefile *.rst *.py *.bat
recursive-include docs *.css *.rst *.py

exclude src/pip/_vendor/six
exclude src/pip/_vendor/six/moves
Expand Down
165 changes: 165 additions & 0 deletions docs/docs_feedback_sphinxext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""A sphinx extension for collecting per doc feedback."""

from __future__ import annotations

from itertools import chain
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Dict, List, Union

from sphinx.application import Sphinx


DEFAULT_DOC_LINES_THRESHOLD = 250
RST_INDENT = 4
EMAIL_INDENT = 6


def _modify_rst_document_source_on_read(
app: Sphinx,
docname: str,
source: List[str],
) -> None:
"""Add info block to top and bottom of each document source.
This function modifies RST source in-place by adding an admonition
block at the top and the bottom of each document right after it's
been read from disk preserving :orphan: at top, if present.
"""
admonition_type = app.config.docs_feedback_admonition_type
big_doc_lines = app.config.docs_feedback_big_doc_lines
escaped_email = app.config.docs_feedback_email.replace(' ', r'\ ')
excluded_documents = set(app.config.docs_feedback_excluded_documents)
questions_list = app.config.docs_feedback_questions_list

valid_admonitions = {
'attention', 'caution', 'danger', 'error', 'hint',
'important', 'note', 'tip', 'warning', 'admonition',
}

if admonition_type not in valid_admonitions:
raise ValueError(
'Expected `docs_feedback_admonition_type` to be one of '
f'{valid_admonitions} but got {admonition_type}.'
)

if not questions_list:
raise ValueError(
'Expected `docs_feedback_questions_list` to list questions '
'but got none.'
)

if docname in excluded_documents:
# NOTE: Completely ignore any document
# NOTE: listed in 'docs_feedback_excluded_documents'.
return

is_doc_big = source[0].count('\n') >= big_doc_lines

questions_list_rst = '\n'.join(
f'{" " * RST_INDENT}{number!s}. {question}'
for number, question in enumerate(questions_list, 1)
)
questions_list_urlencoded = (
'\n'.join(
f'\n{" " * RST_INDENT}{number!s}. {question} '
for number, question in enumerate(
chain(
(f'Document: {docname}. Page URL: https://', ),
questions_list,
),
)
).
rstrip('\r\n\t ').
replace('\r', '%0D').
replace('\n', '%0A').
replace(' ', '%20')
)

admonition_msg = rf"""
**Did this article help?**
We are currently doing research to improve pip's documentation
and would love your feedback.
Please `email us`_ and let us know{{let_us_know_ending}}
{{questions_list_rst}}
.. _email us:
mailto:{escaped_email}\
?subject=[Doc:\ {docname}]\ Pip\ docs\ feedback\ \
(URL\:\ https\://)\
&body={questions_list_urlencoded}
"""
let_us_know_ending = ':'

info_block_bottom = (
f'.. {admonition_type}::\n\t\t{admonition_msg.format_map(locals())}\n'
)

questions_list_rst = ''
let_us_know_ending = (
' why you came to this page and what on it helped '
'you and what did not. '
'(:issue:`Read more about this research <8517>`)'
)
info_block_top = '' if is_doc_big else (
f'.. {admonition_type}::\n\t\t{admonition_msg.format_map(locals())}\n'
)

orphan_mark = ':orphan:'
is_orphan = orphan_mark in source[0]
if is_orphan:
source[0].replace(orphan_mark, '')
else:
orphan_mark = ''

source[0] = '\n\n'.join((
orphan_mark, info_block_top, source[0], info_block_bottom,
))


def setup(app: Sphinx) -> Dict[str, Union[bool, str]]:
"""Initialize the Sphinx extension.
This function adds a callback for modifying the document sources
in-place on read.
It also declares the extension settings changable via :file:`conf.py`.
"""
rebuild_trigger = 'html' # rebuild full html on settings change
app.add_config_value(
'docs_feedback_admonition_type',
default='important',
rebuild=rebuild_trigger,
)
app.add_config_value(
'docs_feedback_big_doc_lines',
default=DEFAULT_DOC_LINES_THRESHOLD,
rebuild=rebuild_trigger,
)
app.add_config_value(
'docs_feedback_email',
default='Docs UX Team <docs-feedback+ux/pip.pypa.io@pypa.io>',
rebuild=rebuild_trigger,
)
app.add_config_value(
'docs_feedback_excluded_documents',
default=set(),
rebuild=rebuild_trigger,
)
app.add_config_value(
'docs_feedback_questions_list',
default=(),
rebuild=rebuild_trigger,
)

app.add_css_file('important-admonition.css')
app.connect('source-read', _modify_rst_document_source_on_read)

return {
'parallel_read_safe': True,
'parallel_write_safe': True,
'version': 'builtin',
}
8 changes: 8 additions & 0 deletions docs/html/_static/important-admonition.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.admonition.important {
background-color: rgb(219, 250, 244);
border: 1px solid rgb(26, 188, 156);
}

.admonition.important>.admonition-title {
color: rgb(26, 188, 156);
}
24 changes: 22 additions & 2 deletions docs/html/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
# extensions = ['sphinx.ext.autodoc']
extensions = [
# native:
'sphinx.ext.extlinks',
'pip_sphinxext',
'sphinx.ext.intersphinx',
# third-party:
'sphinx_tabs.tabs',
# in-tree:
'docs_feedback_sphinxext',
'pip_sphinxext',
]

# intersphinx
Expand Down Expand Up @@ -177,7 +181,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
Expand Down Expand Up @@ -308,3 +312,19 @@ def to_document_name(path, base_dir):
)

man_pages.append((fname_base, outname, description, u'pip developers', 1))

# -- Options for docs_feedback_sphinxext --------------------------------------

# NOTE: Must be one of 'attention', 'caution', 'danger', 'error', 'hint',
# NOTE: 'important', 'note', 'tip', 'warning' or 'admonition'.
docs_feedback_admonition_type = 'important'
docs_feedback_big_doc_lines = 50 # bigger docs will have a banner on top
docs_feedback_email = 'Docs UX Team <docs-feedback+ux/pip.pypa.io@pypa.io>'
docs_feedback_excluded_documents = { # these won't have any banners
'news',
}
docs_feedback_questions_list = (
'What problem were you trying to solve when you came to this page?',
'What content was useful?',
'What content was not useful?',
)
1 change: 1 addition & 0 deletions news/8783.doc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added initial UX feedback widgets to docs.
1 change: 1 addition & 0 deletions news/8848.doc

0 comments on commit fe2075b

Please sign in to comment.