From 98631bb38a423a4a1dcc3b14d061a94df0b94d4d Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 7 Jul 2023 13:16:59 +0200 Subject: [PATCH 01/17] switch to book theme --- .pre-commit-config.yaml | 5 +- .prettierrc | 7 + docs/conf.py | 20 ++- pyproject.toml | 7 +- src/scanpydoc/rtd_github_links.py | 36 +++-- src/scanpydoc/theme/__init__.py | 10 +- src/scanpydoc/theme/layout.html | 56 ++++---- src/scanpydoc/theme/static/css/scanpy.css | 167 ++-------------------- src/scanpydoc/theme/theme.conf | 2 +- 9 files changed, 92 insertions(+), 218 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d737e2..cff7f84 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,6 +14,9 @@ repos: - id: ruff args: ["--fix"] - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 + rev: v3.0.0 hooks: - id: prettier + additional_dependencies: + - prettier@2.8.1 # plugin not yet 3.0 compatible + - prettier-plugin-jinja-template diff --git a/.prettierrc b/.prettierrc index bc27cb2..c9dca38 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,5 @@ { + "plugins": ["prettier-plugin-jinja-template"], "overrides": [ { "files": ["settings.json", ".prettierrc"], @@ -9,5 +10,11 @@ "trailingComma": "all", }, }, + { + "files": ["*.html"], + "options": { + "parser": "jinja-template", + }, + }, ], } diff --git a/docs/conf.py b/docs/conf.py index 090f19b..6e91a4a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,22 +1,16 @@ -import sys from datetime import datetime +from importlib.metadata import metadata from pathlib import Path from sphinx.application import Sphinx -# Allow importing scanpydoc itself HERE = Path(__file__).parent -sys.path.insert(0, str(HERE.parent)) -import scanpydoc # noqa - # Clean build env for file in HERE.glob("scanpydoc.*.rst"): file.unlink() - -needs_sphinx = "1.7" # autosummary bugfix extensions = [ "sphinx.ext.intersphinx", "sphinx.ext.napoleon", @@ -30,7 +24,7 @@ python=("https://docs.python.org/3", None), jinja=("https://jinja.palletsprojects.com/en/2.10.x/", None), sphinx=("https://www.sphinx-doc.org/en/master/", None), - sphinx_rtd_theme=("https://sphinx-rtd-theme.readthedocs.io/en/stable/", None), + sphinx_book_theme=("https://sphinx-book-theme.readthedocs.io/en/stable/", None), # examples numpy=("https://numpy.org/doc/stable/", None), anndata=("https://anndata.readthedocs.io/en/latest/", None), @@ -39,10 +33,11 @@ ) # general information -project = scanpydoc.__name__ -author = "Philipp Angerer" +meta = metadata("scanpydoc") +project = meta["name"] +author = meta["author-email"].split(" <")[0] copyright = f"{datetime.now():%Y}, {author}." -version = release = scanpydoc.__version__ +version = release = meta["version"] master_doc = "index" templates_path = ["_templates"] @@ -54,7 +49,8 @@ html_theme = "scanpydoc" html_context = dict( - github_user="theislab", github_repo="scanpydoc", github_version="main" + repository_url="https://github.com/theislab/scanpydoc", + repository_branch="main", ) # proj/doc/conf.py/../.. → proj diff --git a/pyproject.toml b/pyproject.toml index 0b72dd1..29a35f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,12 +30,11 @@ test = [ 'pytest-cov', ] doc = [ - 'scanpydoc[typehints]', - 'sphinx<7', # https://github.com/readthedocs/sphinx_rtd_theme/issues/1463 - 'sphinx-rtd-theme', + 'scanpydoc[typehints,theme]', + 'sphinx', ] typehints = ['sphinx-autodoc-typehints>=1.15.2'] -theme = ['sphinx-rtd-theme'] +theme = ['sphinx-book-theme>=0.3.3'] [project.entry-points.'sphinx.html_themes'] scanpydoc = 'scanpydoc.theme' diff --git a/src/scanpydoc/rtd_github_links.py b/src/scanpydoc/rtd_github_links.py index b9402f2..c51499e 100644 --- a/src/scanpydoc/rtd_github_links.py +++ b/src/scanpydoc/rtd_github_links.py @@ -18,13 +18,13 @@ The ``project_dir`` is used to figure out the .py file path relative to the git root, that is to construct the path in the github URL. -The ``html_context`` is e.g. also used like this in the sphinx_rtd_theme_. +The ``html_context`` is e.g. also used like this in the :doc:`sphinx_book_theme:index`. Usage ----- You can use the filter e.g. in `autosummary templates`_. -To configure the sphinx_rtd_theme_, +To configure the :doc:`sphinx_book_theme:index`, override the ``autosummary/base.rst`` template like this: .. code:: restructuredtext @@ -35,7 +35,6 @@ .. _autosummary templates: \ http://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html#customizing-templates -.. _sphinx_rtd_theme: https://sphinx-rtd-theme.readthedocs.io/en/latest/ """ from __future__ import annotations @@ -60,9 +59,14 @@ def _init_vars(app: Sphinx, config: Config): """Called when ``conf.py`` has been loaded.""" global github_base_url, project_dir _check_html_context(config) - github_base_url = "https://github.com/{github_user}/{github_repo}/tree/{github_version}".format_map( - config.html_context - ) + try: + github_base_url = "https://github.com/{github_user}/{github_repo}/tree/{github_version}".format_map( + config.html_context + ) + except KeyError: + github_base_url = "{repository_url}/tree/{repository_branch}".format_map( + config.html_context + ) project_dir = Path(config.project_dir) @@ -132,20 +136,22 @@ def github_url(qualname: str) -> str: def _check_html_context(config: Config): try: - html_context = config.html_context + html_context: dict[str, Any] = config.html_context except AttributeError: raise ValueError( f"Extension {__name__} needs “html_context” to be defined in conf.py" ) - missing_values = { - "github_user", - "github_repo", - "github_version", - } - html_context.keys() - if missing_values: - mvs = ", ".join([f"html_context[{mv!r}]" for mv in missing_values]) + options = [ + {"github_user", "github_repo", "github_version"}, + {"repository_url", "repository_branch"}, + ] + missing_value_sets = [opt - html_context.keys() for opt in options] + if all(missing_value_sets): + mvs = " or ".join( + ", ".join(repr(mv) for mv in mvs) for mvs in missing_value_sets + ) raise ValueError( - f"Extension {__name__} needs “{mvs}” to be defined in conf.py.\n" + f"Extension {__name__} needs html_context {mvs} to be defined in conf.py.\n" f"html_context = {html_context!r}" ) diff --git a/src/scanpydoc/theme/__init__.py b/src/scanpydoc/theme/__init__.py index d05b6b5..83f1624 100644 --- a/src/scanpydoc/theme/__init__.py +++ b/src/scanpydoc/theme/__init__.py @@ -1,4 +1,4 @@ -"""A widescreen extension for :doc:`sphinx_rtd_theme:index`. +"""A widescreen extension for :doc:`sphinx_book_theme:index`. Add to ``conf.py``: @@ -19,14 +19,14 @@ The CSS color used for the mobile header background and the project name text. -See ``sphinx_rtd_theme``’s :doc:`sphinx_rtd_theme:configuring`, e.g.: +See ``sphinx_book_theme``’s :doc:`sphinx_book_theme:reference`, e.g.: .. code:: python html_theme_options = dict( - logo_only=False, - accent_color='rebeccapurple', - display_version=False, + repository_url="https://github.com/theislab/scanpydoc", + repository_branch="main", + use_download_button=False, ) Docsearch options diff --git a/src/scanpydoc/theme/layout.html b/src/scanpydoc/theme/layout.html index a950566..ef05987 100644 --- a/src/scanpydoc/theme/layout.html +++ b/src/scanpydoc/theme/layout.html @@ -1,33 +1,39 @@ -{% extends "sphinx_rtd_theme/layout.html" %} +{% extends "sphinx_book_theme/layout.html" %} - -{%- block htmltitle %} {{ super() }} {% if theme_docsearch_key and theme_docsearch_index -%} - -{% endif %} {%- endblock -%} +{%- block css %} +{{ super() }} +{% if theme_docsearch_key and theme_docsearch_index %} + +{% endif %} +{%- endblock -%} -{%- block extrahead %} {{ super() }} {% if theme_accent_color %} +{%- block extrahead %} +{{ super() }} +{% if theme_accent_color %} -{% endif %} {% endblock %} {% set safe_version = version if version in ["latest", -"stable"] else "latest" %} {% block scripts %} {{ super() }} {% if theme_docsearch_key -and theme_docsearch_index %} +{% endif %} +{% endblock %} + +{% set safe_version = version if version in ["latest", "stable"] else "latest" %} + +{% block scripts %} +{{ super() }} +{% if theme_docsearch_key and theme_docsearch_index %} -{% endif %} {% endblock %} +{% endif %} +{% endblock %} diff --git a/src/scanpydoc/theme/static/css/scanpy.css b/src/scanpydoc/theme/static/css/scanpy.css index b3a4971..5c68a8c 100644 --- a/src/scanpydoc/theme/static/css/scanpy.css +++ b/src/scanpydoc/theme/static/css/scanpy.css @@ -1,163 +1,20 @@ -@import "theme.css"; - -/* ReadTheDocs theme global changes */ - -:root { - --accent-color: #f07e44; -} -.wy-nav-top { - background-color: var(--accent-color); -} -.wy-nav-content { - max-width: 1200px; -} -.wy-side-nav-search { - background-color: transparent; -} -.wy-side-nav-search input[type="text"] { - border-width: 0; -} -.wy-side-nav-search a.icon-home { - font-size: 2em; - color: var(--accent-color); -} -.wy-side-nav-search a.icon-home::before { - content: none; -} -.wy-menu li a { - /* display ellipsis in long items */ - overflow: hidden; - text-overflow: ellipsis; +/* for the sphinx design cards */ +body { + --sd-color-shadow: dimgrey; } -/* DocSearch adaptations */ - -.wy-nav-side { - overflow: visible; -} -.wy-side-scroll { - overflow: inherit; -} - -/* Making it a bit wider */ -.algolia-autocomplete .ds-dropdown-menu { - max-width: 1000px; - min-width: 80vw; -} - -/* Custom classes */ - -.small { - font-size: 40%; -} -.smaller, -.pr, -.issue { - font-size: 70%; -} - -/* Custom classes with bootstrap buttons */ - -.tutorial, -.tutorial:visited, -.tutorial:hover { - /* text-decoration: underline; */ +dl.citation > dt { + float: left; + margin-right: 15px; font-weight: bold; - padding: 2px 5px; - white-space: nowrap; - max-width: 100%; - background: #ef3270; /* color from the tail of the scampy */ - border: solid 1px #ef3270; - border-radius: 0.25rem; - font-size: 75%; - /* font-family: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",Courier,monospace; */ - color: #404040; - overflow-x: auto; - box-sizing: border-box; } -/* Formatting of RTD markup: rubrics and sidebars and admonitions */ - -/* sidebar */ -.rst-content .sidebar { - /* margin: 0px 0px 0px 12px; */ - padding-bottom: 0px; -} -.rst-content .sidebar p { - margin-bottom: 12px; -} -.rst-content .sidebar p, -.rst-content .sidebar ul, -.rst-content .sidebar dl { - font-size: 13px; -} - -/* less space after bullet lists in admonitions like warnings and notes */ -.rst-content .section .admonition ul { - margin-bottom: 6px; -} - -/* Code: literals and links */ - -.rst-content tt.literal, -.rst-content code.literal { - color: #404040; -} -/* slim font weight for non-link code */ -.rst-content tt:not(.xref), -.rst-content code:not(.xref), -.rst-content *:not(a) > tt.xref, -.rst-content *:not(a) > code.xref, -.rst-content dl:not(.docutils) code { - font-weight: normal; -} -.rst-content a > tt.xref, -.rst-content a > code.xref, -.rst-content dl:not(.docutils) a > tt.xref, -.rst-content dl:not(.docutils) a > code.xref { - font-weight: bold; /* underline looks clumsy, in particular with buttons and - other hyperlinks, which don't come with underlines */ -} - -/* Just one box for annotation code for a less noisy look */ - -.rst-content .annotation { - padding: 2px 5px; - background-color: white; - border: 1px solid #e1e4e5; -} -.rst-content .annotation tt, -.rst-content .annotation code { - padding: 0 0; - background-color: transparent; - border: 0 solid transparent; -} - -/* Parameter lists */ - -/* Mimick rubric style used for other headings */ -/* TODO: once scanpydoc adds classes, also change return types like this */ -.rst-content dl:not(.docutils) dl > dt { - font-weight: bold; - background: none transparent; - border-left: none; - margin: 0 0 12px; - padding: 3px 0 0; - font-size: 111.11%; -} -/* Parameters contain parts and don’t need bold font */ -.rst-content dl.field-list dl > dt { - font-weight: unset; -} -/* Add colon between return tuple element name and type */ -.rst-content dl:not(.docutils) dl > dt .classifier::before { - content: " : "; +/* for custom small role */ +.small { + font-size: 40% !important; } -/* Function headers */ - -.rst-content dl:not(.docutils) dt { - background: #edf0f2; - color: #404040; - border-top: solid 3px #343131; +.smaller, +.pr { + font-size: 70% !important; } diff --git a/src/scanpydoc/theme/theme.conf b/src/scanpydoc/theme/theme.conf index abbec85..55e0d45 100644 --- a/src/scanpydoc/theme/theme.conf +++ b/src/scanpydoc/theme/theme.conf @@ -1,5 +1,5 @@ [theme] -inherit = sphinx_rtd_theme +inherit = sphinx_book_theme stylesheet = css/scanpy.css pygments_style = default From 1b77f709f3c6c57c278027c95fac8493449dc691 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 7 Jul 2023 13:35:27 +0200 Subject: [PATCH 02/17] ignore html --- .prettierignore | 2 ++ .prettierrc | 20 -------------------- .prettierrc.yaml | 12 ++++++++++++ 3 files changed, 14 insertions(+), 20 deletions(-) create mode 100644 .prettierignore delete mode 100644 .prettierrc create mode 100644 .prettierrc.yaml diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..16ebbd7 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# https://github.com/davidodenwald/prettier-plugin-jinja-template/issues/14 +*.html diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index c9dca38..0000000 --- a/.prettierrc +++ /dev/null @@ -1,20 +0,0 @@ -{ - "plugins": ["prettier-plugin-jinja-template"], - "overrides": [ - { - "files": ["settings.json", ".prettierrc"], - "options": { - "parser": "json5", - "quoteProps": "preserve", - "singleQuote": false, - "trailingComma": "all", - }, - }, - { - "files": ["*.html"], - "options": { - "parser": "jinja-template", - }, - }, - ], -} diff --git a/.prettierrc.yaml b/.prettierrc.yaml new file mode 100644 index 0000000..84ed789 --- /dev/null +++ b/.prettierrc.yaml @@ -0,0 +1,12 @@ +plugins: + - prettier-plugin-jinja-template +overrides: + - files: [settings.json] + options: + parser: json5 + quoteProps: preserve + singleQuote: false + trailingComma: all + - files: ["*.html"] + options: + parser: jinja-template From a3dd7fa3f34e59889d23c3eb814b42d7f06f3ee2 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 7 Jul 2023 13:42:59 +0200 Subject: [PATCH 03/17] options --- pyproject.toml | 2 +- src/scanpydoc/theme/theme.conf | 70 ++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 29a35f9..bb18d12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ doc = [ 'sphinx', ] typehints = ['sphinx-autodoc-typehints>=1.15.2'] -theme = ['sphinx-book-theme>=0.3.3'] +theme = ['sphinx-book-theme>=1.0.1'] [project.entry-points.'sphinx.html_themes'] scanpydoc = 'scanpydoc.theme' diff --git a/src/scanpydoc/theme/theme.conf b/src/scanpydoc/theme/theme.conf index 55e0d45..17dc7da 100644 --- a/src/scanpydoc/theme/theme.conf +++ b/src/scanpydoc/theme/theme.conf @@ -4,18 +4,66 @@ stylesheet = css/scanpy.css pygments_style = default [options] -canonical_url = -analytics_id = -collapse_navigation = True -sticky_navigation = True +# https://github.com/pydata/pydata-sphinx-theme/blob/v0.13.3/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf +sidebarwidth = 270 +sidebar_includehidden = True +use_edit_page_button = False +external_links = +bitbucket_url = +github_url = +gitlab_url = +twitter_url = +icon_links_label = Icon Links +icon_links = +analytics = +favicons = +show_prev_next = True +search_bar_text = Search the docs ... +search_bar_position = sidebar +navigation_with_keys = True +collapse_navigation = False navigation_depth = 4 -includehidden = True -titles_only = -logo_only = -display_version = True -prev_next_buttons_location = bottom -style_external_links = False -style_nav_header_background = +show_nav_level = 1 +show_toc_level = 1 +navbar_align = content +header_links_before_dropdown = 5 +switcher = +check_switcher = True +pygment_light_style = a11y-high-contrast-light +pygment_dark_style = a11y-high-contrast-dark +logo = +article_footer_items = +content_footer_items = +primary_sidebar_end = sidebar-ethical-ads.html + +# https://github.com/executablebooks/sphinx-book-theme/blob/v1.0.1/src/sphinx_book_theme/theme/sphinx_book_theme/theme.conf +announcement = +secondary_sidebar_items = page-toc.html +toc_title = Contents +article_header_start = toggle-primary-sidebar.html +article_header_end = article-header-buttons.html +use_download_button = True +use_fullscreen_button = True +use_issues_button = False +use_source_button = False +use_repository_button = False +path_to_docs = +repository_url = +repository_branch = +repository_provider = +launch_buttons = {} +navbar_start = +navbar_center = +navbar_end = +navbar_persistent = +home_page_in_toc = False +show_navbar_depth = 1 +extra_footer = +footer_content_items = author.html, copyright.html, last-updated.html, extra-footer.html +footer_start = +footer_end = +use_sidenotes = False + # added by scanpydoc accent_color = docsearch_key = From cdf5bed443b83dc855f3912335c0454a26ae2a62 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 7 Jul 2023 14:04:28 +0200 Subject: [PATCH 04/17] Fix styles --- docs/conf.py | 5 +++-- src/scanpydoc/theme/__init__.py | 2 +- src/scanpydoc/theme/static/{css => styles}/scanpy.css | 2 ++ src/scanpydoc/theme/theme.conf | 5 +++-- 4 files changed, 9 insertions(+), 5 deletions(-) rename src/scanpydoc/theme/static/{css => styles}/scanpy.css (89%) diff --git a/docs/conf.py b/docs/conf.py index 6e91a4a..db3eff7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,10 +51,11 @@ html_context = dict( repository_url="https://github.com/theislab/scanpydoc", repository_branch="main", + use_repository_button=True, ) -# proj/doc/conf.py/../.. → proj -project_dir = Path(__file__).parent.parent +# proj/doc/.. → proj +project_dir = HERE.parent def setup(app: Sphinx): diff --git a/src/scanpydoc/theme/__init__.py b/src/scanpydoc/theme/__init__.py index 83f1624..0f277f2 100644 --- a/src/scanpydoc/theme/__init__.py +++ b/src/scanpydoc/theme/__init__.py @@ -26,7 +26,7 @@ html_theme_options = dict( repository_url="https://github.com/theislab/scanpydoc", repository_branch="main", - use_download_button=False, + use_repository_button=True, ) Docsearch options diff --git a/src/scanpydoc/theme/static/css/scanpy.css b/src/scanpydoc/theme/static/styles/scanpy.css similarity index 89% rename from src/scanpydoc/theme/static/css/scanpy.css rename to src/scanpydoc/theme/static/styles/scanpy.css index 5c68a8c..fd65f43 100644 --- a/src/scanpydoc/theme/static/css/scanpy.css +++ b/src/scanpydoc/theme/static/styles/scanpy.css @@ -1,3 +1,5 @@ +@import "sphinx-book-theme.css"; + /* for the sphinx design cards */ body { --sd-color-shadow: dimgrey; diff --git a/src/scanpydoc/theme/theme.conf b/src/scanpydoc/theme/theme.conf index 17dc7da..c71edd5 100644 --- a/src/scanpydoc/theme/theme.conf +++ b/src/scanpydoc/theme/theme.conf @@ -1,7 +1,8 @@ [theme] inherit = sphinx_book_theme -stylesheet = css/scanpy.css -pygments_style = default +pygments_style = tango +sidebars = navbar-logo.html, icon-links.html, sbt-sidebar-nav.html +stylesheet = styles/scanpy.css [options] # https://github.com/pydata/pydata-sphinx-theme/blob/v0.13.3/src/pydata_sphinx_theme/theme/pydata_sphinx_theme/theme.conf From 73c8133e577c7e8dd243eb3978bea26b5882d2b9 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 7 Jul 2023 14:23:02 +0200 Subject: [PATCH 05/17] Fix names --- src/scanpydoc/rtd_github_links.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scanpydoc/rtd_github_links.py b/src/scanpydoc/rtd_github_links.py index c51499e..08a4dc3 100644 --- a/src/scanpydoc/rtd_github_links.py +++ b/src/scanpydoc/rtd_github_links.py @@ -10,21 +10,21 @@ project_dir: Path = ... # default: Path.cwd() html_context = dict( - github_user=..., - github_repo=..., - github_version=..., + repository_url=..., + repository_branch=..., ) The ``project_dir`` is used to figure out the .py file path relative to the git root, that is to construct the path in the github URL. -The ``html_context`` is e.g. also used like this in the :doc:`sphinx_book_theme:index`. +The ``html_context`` is e.g. also used like this in the +:doc:`Sphinx Book Theme `. Usage ----- You can use the filter e.g. in `autosummary templates`_. -To configure the :doc:`sphinx_book_theme:index`, +To configure the :doc:`Sphinx Book Theme `, override the ``autosummary/base.rst`` template like this: .. code:: restructuredtext From d4e94a1103115e5c45eb52841bbd00114d627d32 Mon Sep 17 00:00:00 2001 From: Phil Schaf Date: Thu, 13 Jul 2023 13:33:28 +0200 Subject: [PATCH 06/17] prettier html --- .prettierignore | 2 -- src/scanpydoc/theme/layout.html | 63 ++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 30 deletions(-) delete mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 16ebbd7..0000000 --- a/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -# https://github.com/davidodenwald/prettier-plugin-jinja-template/issues/14 -*.html diff --git a/src/scanpydoc/theme/layout.html b/src/scanpydoc/theme/layout.html index ef05987..e0993d6 100644 --- a/src/scanpydoc/theme/layout.html +++ b/src/scanpydoc/theme/layout.html @@ -1,39 +1,46 @@ {% extends "sphinx_book_theme/layout.html" %} -{%- block css %} -{{ super() }} -{% if theme_docsearch_key and theme_docsearch_index %} - -{% endif %} +{%- block css -%} + {{ super() }} + {% if theme_docsearch_key and theme_docsearch_index %} + + {% endif %} {%- endblock -%} -{%- block extrahead %} -{{ super() }} -{% if theme_accent_color %} - -{% endif %} +{%- block extrahead -%} + {{ super() }} + {% if theme_accent_color %} + + + + {% endif %} {% endblock %} {% set safe_version = version if version in ["latest", "stable"] else "latest" %} {% block scripts %} -{{ super() }} -{% if theme_docsearch_key and theme_docsearch_index %} - - -{% endif %} + {{ super() }} + {% if theme_docsearch_key and theme_docsearch_index %} + + + + + {% endif %} {% endblock %} From 0f06db3d05e4411d1b053f61070d27557b019571 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 15:53:37 +0200 Subject: [PATCH 07/17] linkcode --- docs/conf.py | 1 + .../__init__.py} | 26 ++++++++++++++-- src/scanpydoc/rtd_github_links/_linkcode.py | 31 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) rename src/scanpydoc/{rtd_github_links.py => rtd_github_links/__init__.py} (89%) create mode 100644 src/scanpydoc/rtd_github_links/_linkcode.py diff --git a/docs/conf.py b/docs/conf.py index db3eff7..b430e45 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ "sphinx_autodoc_typehints", # needs to be after napoleon "sphinx.ext.autodoc", "sphinx.ext.autosummary", + "sphinx.ext.linkcode", "scanpydoc", ] diff --git a/src/scanpydoc/rtd_github_links.py b/src/scanpydoc/rtd_github_links/__init__.py similarity index 89% rename from src/scanpydoc/rtd_github_links.py rename to src/scanpydoc/rtd_github_links/__init__.py index 08a4dc3..43a7ace 100644 --- a/src/scanpydoc/rtd_github_links.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -1,7 +1,19 @@ """GitHub URLs for class and method pages. -This extension registers a :ref:`Jinja filter ` called :func:`github_url` -that you can use to convert a module path into a GitHub URL +This extension does two things: + +#. It registers a :ref:`Jinja filter ` called :func:`github_url` + that you can use to convert a module path into a GitHub URL. +#. It configures `sphinx.ext.linkcode` for you if loaded before this one: + + .. code:: python + + extensions = [ + "sphinx.ext.linkcode", + "scanpydoc", + ] + + # no need to define `linkcode_resolve` Configuration ------------- @@ -48,7 +60,7 @@ from sphinx.application import Sphinx from sphinx.config import Config -from . import _setup_sig, metadata +from .. import _setup_sig, metadata project_dir = None # type: Path @@ -169,6 +181,14 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value("project_dir", proj_dir, "") app.connect("config-inited", _init_vars) + if ( + "linkcode_resolve" in app.config.values + and app.config["linkcode_resolve"] is not None + ): + from ._linkcode import linkcode_resolve + + app.config["linkcode_resolve"] = linkcode_resolve + # html_context doesn’t apply to autosummary templates ☹ # and there’s no way to insert filters into those templates # so we have to modify the default filters diff --git a/src/scanpydoc/rtd_github_links/_linkcode.py b/src/scanpydoc/rtd_github_links/_linkcode.py new file mode 100644 index 0000000..2fa6edd --- /dev/null +++ b/src/scanpydoc/rtd_github_links/_linkcode.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import Literal, TypedDict + + +class PyInfo(TypedDict): + module: str + fullname: str + + +class CInfo(TypedDict): + """C / C++ info.""" + + names: list[str] + + +class JSInfo(TypedDict): + object: str + fullname: str + + +def linkcode_resolve( + domain: Literal["py", "c", "cpp", "javascript"], info: PyInfo | CInfo | JSInfo +) -> str | None: + from . import github_url + + if domain != "py": + return None + if not info["module"]: + return None + return github_url(f'{info["module"]}.{info["fullname"]}') From 228ef7e65fdaf71ea47bba87179395880eb13099 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 15:54:27 +0200 Subject: [PATCH 08/17] settings --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index b451cc6..01d59a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll": true, + "source.organizeImports": true, }, }, } From 54a73a592663b1c35991c9e178ed9163c835a90d Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 15:57:15 +0200 Subject: [PATCH 09/17] fix tests --- tests/test_rtd_github_links.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/test_rtd_github_links.py b/tests/test_rtd_github_links.py index 65e6be6..9df6dcf 100644 --- a/tests/test_rtd_github_links.py +++ b/tests/test_rtd_github_links.py @@ -17,12 +17,10 @@ def env(monkeypatch: MonkeyPatch): def test_as_function(env): - assert github_url("scanpydoc.rtd_github_links") == "./scanpydoc/rtd_github_links.py" + pth = "./scanpydoc/rtd_github_links/__init__.py" + assert github_url("scanpydoc.rtd_github_links") == pth s, e = _get_linenos(github_url) - assert ( - github_url("scanpydoc.rtd_github_links.github_url") - == f"./scanpydoc/rtd_github_links.py#L{s}-L{e}" - ) + assert github_url("scanpydoc.rtd_github_links.github_url") == f"{pth}#L{s}-L{e}" def test_get_obj_module(): From 09c1a1168b5cdcd97740d2b0e00c28db883afadd Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:09:35 +0200 Subject: [PATCH 10/17] Fix --- docs/conf.py | 2 +- pyproject.toml | 2 +- src/scanpydoc/rtd_github_links/__init__.py | 10 ++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b430e45..f874f4b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,8 +17,8 @@ "sphinx_autodoc_typehints", # needs to be after napoleon "sphinx.ext.autodoc", "sphinx.ext.autosummary", - "sphinx.ext.linkcode", "scanpydoc", + "sphinx.ext.linkcode", # needs to be after scanpydoc ] intersphinx_mapping = dict( diff --git a/pyproject.toml b/pyproject.toml index bb18d12..61323a1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ python = ['3.8', '3.9', '3.10', '3.11'] [tool.hatch.envs.test] features = ['test', 'typehints'] [tool.hatch.envs.test.scripts] -test = 'pytest -vv' +test = 'pytest -vv {args}' [tool.pytest.ini_options] addopts = [ diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index 43a7ace..e3fb00c 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -4,13 +4,13 @@ #. It registers a :ref:`Jinja filter ` called :func:`github_url` that you can use to convert a module path into a GitHub URL. -#. It configures `sphinx.ext.linkcode` for you if loaded before this one: +#. It configures `sphinx.ext.linkcode` for you if loaded after it: .. code:: python extensions = [ - "sphinx.ext.linkcode", "scanpydoc", + "sphinx.ext.linkcode", ] # no need to define `linkcode_resolve` @@ -181,10 +181,8 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_config_value("project_dir", proj_dir, "") app.connect("config-inited", _init_vars) - if ( - "linkcode_resolve" in app.config.values - and app.config["linkcode_resolve"] is not None - ): + # if linkcode config not set + if "linkcode_resolve" not in app.config or app.config["linkcode_resolve"] is None: from ._linkcode import linkcode_resolve app.config["linkcode_resolve"] = linkcode_resolve From b472fef2b6a84c59ac16f75f517c6079bafe08a9 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:16:01 +0200 Subject: [PATCH 11/17] really fix tests --- pyproject.toml | 2 +- tests/test_rtd_github_links.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 61323a1..6a617b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ python = ['3.8', '3.9', '3.10', '3.11'] [tool.hatch.envs.test] features = ['test', 'typehints'] [tool.hatch.envs.test.scripts] -test = 'pytest -vv {args}' +run = 'pytest -vv {args}' [tool.pytest.ini_options] addopts = [ diff --git a/tests/test_rtd_github_links.py b/tests/test_rtd_github_links.py index 9df6dcf..1392c92 100644 --- a/tests/test_rtd_github_links.py +++ b/tests/test_rtd_github_links.py @@ -13,11 +13,11 @@ @pytest.fixture def env(monkeypatch: MonkeyPatch): monkeypatch.setattr("scanpydoc.rtd_github_links.github_base_url", ".") - monkeypatch.setattr("scanpydoc.rtd_github_links.project_dir", HERE) + monkeypatch.setattr("scanpydoc.rtd_github_links.project_dir", HERE.parent) def test_as_function(env): - pth = "./scanpydoc/rtd_github_links/__init__.py" + pth = "./src/scanpydoc/rtd_github_links/__init__.py" assert github_url("scanpydoc.rtd_github_links") == pth s, e = _get_linenos(github_url) assert github_url("scanpydoc.rtd_github_links.github_url") == f"{pth}#L{s}-L{e}" From 03eab45317a152384564ab883974919ea7aa5542 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:21:52 +0200 Subject: [PATCH 12/17] support src layout --- src/scanpydoc/rtd_github_links/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index e3fb00c..9b37137 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -141,6 +141,8 @@ def github_url(qualname: str) -> str: except ValueError: # trying to document something from another package path = "/".join(module.__file__.split("/")[-2:]) + if (project_dir / "src").is_dir(): + path = "src" / path start, end = _get_linenos(obj) fragment = f"#L{start}-L{end}" if start and end else "" return f"{github_base_url}/{path}{fragment}" From 8a19808ee0900baf863a5874a0f4502566b4c448 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:25:14 +0200 Subject: [PATCH 13/17] oop --- src/scanpydoc/rtd_github_links/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index 9b37137..c7cba87 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -136,11 +136,11 @@ def github_url(qualname: str) -> str: except Exception: print(f"Error in github_url({qualname!r}):", file=sys.stderr) raise - try: + try: # only works when installed in dev mode path = PurePosixPath(Path(module.__file__).resolve().relative_to(project_dir)) except ValueError: - # trying to document something from another package - path = "/".join(module.__file__.split("/")[-2:]) + # no dev mode or something from another package + path = PurePosixPath(module.__file__.split("/")[-2:]) if (project_dir / "src").is_dir(): path = "src" / path start, end = _get_linenos(obj) From 21c406a25fec2c23a60246d8fd6b55953e2291de Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:27:04 +0200 Subject: [PATCH 14/17] editable --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fed3f1e..c024b86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,6 @@ jobs: - name: dependencies run: | pip install --upgrade pip wheel - pip install .[test,typehints] + pip install -e .[test,typehints] - name: tests run: pytest --color=yes From b86fb4e97440f9338590dc41787cef379f939ad5 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:33:58 +0200 Subject: [PATCH 15/17] docs --- docs/conf.py | 4 ++++ src/scanpydoc/rtd_github_links/__init__.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index f874f4b..bc72954 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,4 @@ +import sys from datetime import datetime from importlib.metadata import metadata from pathlib import Path @@ -7,6 +8,9 @@ HERE = Path(__file__).parent +# necessary for rtd_gh_links’ linkcode support +sys.path.insert(0, HERE.parent / "src") + # Clean build env for file in HERE.glob("scanpydoc.*.rst"): file.unlink() diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index c7cba87..8095c24 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -8,6 +8,13 @@ .. code:: python + import sys + from pathlib import Path + + HERE = Path(__file__).parent + # make sure modules are import from the right place + sys.path.insert(0, HERE.parent / "src") + extensions = [ "scanpydoc", "sphinx.ext.linkcode", From 64dac1c59d7858c1644af27bc92f6d29ed69c2ea Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:35:12 +0200 Subject: [PATCH 16/17] fix path creation --- src/scanpydoc/rtd_github_links/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index 8095c24..0b2e9c2 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -147,7 +147,7 @@ def github_url(qualname: str) -> str: path = PurePosixPath(Path(module.__file__).resolve().relative_to(project_dir)) except ValueError: # no dev mode or something from another package - path = PurePosixPath(module.__file__.split("/")[-2:]) + path = PurePosixPath(*module.__file__.split("/")[-2:]) if (project_dir / "src").is_dir(): path = "src" / path start, end = _get_linenos(obj) From 9ff100bcfea11021c50886a9e04713eace1b1ec6 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 18 Jul 2023 16:45:51 +0200 Subject: [PATCH 17/17] docs update --- src/scanpydoc/rtd_github_links/__init__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/scanpydoc/rtd_github_links/__init__.py b/src/scanpydoc/rtd_github_links/__init__.py index 0b2e9c2..ad9ca5a 100644 --- a/src/scanpydoc/rtd_github_links/__init__.py +++ b/src/scanpydoc/rtd_github_links/__init__.py @@ -4,7 +4,7 @@ #. It registers a :ref:`Jinja filter ` called :func:`github_url` that you can use to convert a module path into a GitHub URL. -#. It configures `sphinx.ext.linkcode` for you if loaded after it: +#. It configures :mod:`sphinx.ext.linkcode` for you if loaded after it in ``conf.py``: .. code:: python @@ -28,19 +28,27 @@ Uses the following config values in ``conf.py``:: project_dir: Path = ... # default: Path.cwd() + + # sphinx book theme style html_context = dict( repository_url=..., repository_branch=..., ) + # or RTD theme style: + html_context = dict( + github_user=..., + github_repo=..., + github_version=..., + ) The ``project_dir`` is used to figure out the .py file path relative to the git root, that is to construct the path in the github URL. -The ``html_context`` is e.g. also used like this in the +Which ``html_context`` style you want to use depends on your theme, e.g. :doc:`Sphinx Book Theme `. -Usage ------ +``:github_url:`` usage +---------------------- You can use the filter e.g. in `autosummary templates`_. To configure the :doc:`Sphinx Book Theme `,