.. _LINK/External:
External link icons
###################
.. grid:: 2
.. grid-item::
:columns: 6
In HTML, the extension draws an **icon behind each external link**, so a reader sees that a link leaves the
documentation, and where to, before following it.
The icon is chosen by the link's **target**: GitHub's mark for a link to GitHub, an envelope for an e-mail
address, and a square with an arrow pointing out of it for any host without an icon of its own - see
:ref:`LINK/External/Icons`.
A document needs no markup: every link written with its URI gets its icon, also a link of a
:mod:`sphinx.ext.extlinks` role such as ``:gh:``. The configuration value ``pyTooling_ExternalLink_Icons`` adds
the links to other projects' documentations, or disables the icons - see :ref:`LINK/External/Configuration`.
A marked link **opens in a new browser tab**, unless ``pyTooling_ExternalLink_NewTab`` is ``False``; an e-mail
address opens the mail program, as before.
A **download** gets a download icon - an archive an archive icon - and its **file's size** behind it - see
:ref:`LINK/External/Downloads`.
.. grid-item::
:columns: 6
.. code-block:: ReST
Built with `Sphinx `__,
hosted at :gh:`pyTooling/pyTooling.Sphinx`, published
on `PyPI `__.
This is how the example renders:
Built with `Sphinx `__, hosted at :gh:`pyTooling/pyTooling.Sphinx`, published on
`PyPI `__.
.. _LINK/External/Icons:
Icons
*****
.. list-table::
:header-rows: 1
:widths: 30 15 55
* - Example
- Icon
- Target
* - `Docutils `__
- ``external``
- Any host without an icon of its own, e.g. a site on GitHub Pages, and an intersphinx link with a relative URI.
* - `someone@example.org `__
- ``mail``
- An e-mail address: a URI of scheme ``mailto``.
* - :download:`pyTooling.Sphinx' logo <../_static/logo.png>`
- ``download``
- A link of Sphinx' ``:download:`` role, to a file of the documentation or of another site.
* - :download:`GHDL 6.0.0 for Windows
`
- ``archive``
- A link to an archive: a file name ending in an archive's extension, e.g. ``.zip`` or ``.tar.gz``, in any case -
see :data:`~pyTooling.Sphinx.ExternalLink.ARCHIVE_FORMATS`. It replaces the download icon and the host's icon.
* - :gh:`pyTooling/pyTooling.Sphinx`
- ``github``
- ``github.com``
* - `GitLab `__
- ``gitlab``
- ``gitlab.com``
* - `Python `__, `PyPI `__
- ``python``
- ``python.org``, ``pypi.org``
* - `NumPy `__
- ``numpy``
- ``numpy.org``
* - `PyPy `__
- ``pypy``
- ``pypy.org``
* - `pytest `__
- ``pytest``
- ``pytest.org``
* - `Pydantic `__
- ``pydantic``
- ``pydantic.dev``
* - `Sphinx `__
- ``sphinx``
- ``sphinx-doc.org``
* - `Read the Docs `__,
`its theme's documentation `__
- ``readthedocs``
- ``readthedocs.io``, ``readthedocs.org``, ``rtfd.io``: the projects' documentation hosted there, too
* A host's icon is also the icon of its subdomains: ``python.org`` gives ``docs.python.org`` the Python logo.
* A host is compared in lower case, so ``https://GitHub.com/...`` gets GitHub's mark too.
* A site on GitHub Pages - ``.github.io`` - is the documentation hosted there, not GitHub: it gets the generic
icon.
The hosts with an icon are :data:`~pyTooling.Sphinx.ExternalLink.HOST_ICONS`; ``pyTooling_ExternalLink_Hosts``
adds hosts or replaces an icon.
.. _LINK/External/Links:
Marked links
************
.. list-table::
:header-rows: 1
:widths: 50 50
* - Link
- Icon
* - A link with an absolute URI of scheme ``http``, ``https``, ``ftp`` or ``mailto``: named or anonymous, a
standalone URI or e-mail address, a link of a :mod:`sphinx.ext.extlinks` role.
- Yes, unless ``pyTooling_ExternalLink_Icons`` is ``none``.
* - A link resolved to another project's documentation, e.g. by :mod:`sphinx.ext.intersphinx`.
- If ``pyTooling_ExternalLink_Icons`` is ``all``.
* - A link within the documentation: a cross-reference, or a relative URI written by hand.
- No.
* - A link holding an image, e.g. a badge of the :ref:`shields ` directive, or an image with
``:target:``.
- No.
* - A toctree entry and the navigation.
- No.
* - A link of Sphinx' ``:download:`` role, to a file of the documentation or of another site.
- The download icon, or the archive icon for an archive, unless ``pyTooling_ExternalLink_Icons`` is ``none``. It
stays in the browser's tab, which saves the file.
.. _LINK/External/Downloads:
Downloads
*********
A file to download is linked with Sphinx' ``:download:`` role - a file of the documentation, and another site's file,
e.g. a release's asset on GitHub, alike:
.. code-block:: ReST
Download the :download:`logo <_static/logo.png>`, or :download:`GHDL 6.0.0 for Windows
`.
This is how the example renders - this documentation asks no other site for a size, so the second link has none:
Download the :download:`logo <../_static/logo.png>`, or :download:`GHDL 6.0.0 for Windows
`.
* The link gets the download icon, or the archive icon if the file is an archive. It stays in the browser's tab, which
saves the file.
* The **file's size** follows the link, and for an archive its format: *logo (61.3 kiB)*, *GHDL 6.0.0 for Windows
(ZIP, 22.6 MiB)*. The units are binary - 1 kiB is 1024 bytes -: ``B`` below 1 kiB, then ``kiB``, ``MiB`` and
``GiB`` with one decimal.
.. hint::
Link every download with ``:download:``. A link written with its URI to an archive, e.g. to
``https://example.org/pkg-1.0.zip``, gets the archive icon and the size too, but only as a fallback for documents
that don't follow this rule: a link to any other file, e.g. a PDF, isn't recognized as a download.
.. _LINK/External/Downloads/Local:
Files of the documentation
==========================
The size of a file of the documentation is its size on disk when the documentation is built. Every download gets it,
unless ``pyTooling_ExternalLink_DownloadSizes`` is ``False``. A file Sphinx doesn't find gets no size - an archive its
format only -; Sphinx warns about it.
.. _LINK/External/Downloads/Remote:
Other sites' files
==================
The extension asks another site for a file's size only if the file's URI matches a pattern of
``pyTooling_ExternalLink_RemoteSizes`` - by default, it asks no site; an archive gets its format only. It sends an HTTP
``HEAD`` request with Sphinx' :mod:`sphinx.util.requests`, so Sphinx' configuration values ``user_agent``,
``tls_verify`` and ``tls_cacerts`` apply. It follows redirections - e.g. of a GitHub release's asset to its storage -,
and reads the response's ``Content-Length``. Each URI is asked once per build, however often it is linked.
* A pattern is a glob pattern, compared case-sensitively with the whole URI: ``*`` matches any characters - also
``/`` -, ``?`` one character, and ``[...]`` one of the characters listed.
* Only a URI of scheme ``http`` or ``https`` is asked for.
* The extension waits ``pyTooling_ExternalLink_RemoteSizeTimeout`` seconds for a response, 5 by default.
.. code-block:: Python
# doc/conf.py
pyTooling_ExternalLink_RemoteSizes = [
"https://github.com/pyTooling/*/releases/download/*", # the releases' assets of an organization's projects
"https://example.org/files/*.zip",
]
pyTooling_ExternalLink_RemoteSizeTimeout = 2.5
If the size stays unknown - no response within the time, an HTTP error, or a response without ``Content-Length`` - the
link gets no size, an archive its format only, e.g. ``(ZIP)``, and Sphinx warns at the link, with the warning type
``pyTooling.download_size`` appended in brackets:
.. code-block:: text
a.rst:2: WARNING: Size of download 'https://example.org/a.zip' is unknown. No response within 2.5 seconds.
a.rst:4: WARNING: Size of download 'https://example.org/b.zip' is unknown. Server responded 404 Not Found.
A project that accepts a missing size suppresses the warning by its type:
.. code-block:: Python
# doc/conf.py
suppress_warnings = [
"pyTooling.download_size",
]
.. _LINK/External/Configuration:
Configuration
*************
.. list-table:: Configuration values in :file:`conf.py`
:header-rows: 1
:widths: 35 15 50
* - Name
- Default
- Effect
* - ``pyTooling_ExternalLink_Icons``
- ``"explicit"``
- Which external links get an icon:
``none``
No link.
``explicit``
The links written with their URI, also those of an extlinks role.
``all``
These, and the links resolved to another project's documentation, e.g. by intersphinx.
* - ``pyTooling_ExternalLink_NewTab``
- ``True``
- Whether a link with an icon opens in a new browser tab: it gets ``target="_blank"`` and
``rel="noopener noreferrer"``. An e-mail address never does; a browser would open an empty tab beside the mail
program. A screen reader announces the new tab with the icon, e.g. "external link, opens in a new tab".
* - ``pyTooling_ExternalLink_Hosts``
- ``{}``
- More hosts, each with the name of its icon - see :ref:`LINK/External/Icons`. A host given here wins over a
host of :data:`~pyTooling.Sphinx.ExternalLink.HOST_ICONS`; ``None`` is ``external``.
* - ``pyTooling_ExternalLink_DownloadSizes``
- ``True``
- Whether a download gets its file's size behind its link - see :ref:`LINK/External/Downloads`.
* - ``pyTooling_ExternalLink_RemoteSizes``
- ``[]``
- The glob patterns of the URIs of other sites' files whose size is asked for - see
:ref:`LINK/External/Downloads/Remote`. Without a pattern, no site is asked.
* - ``pyTooling_ExternalLink_RemoteSizeTimeout``
- ``5.0``
- The seconds to wait for a site's response to the request for a size; a positive number.
.. code-block:: Python
# doc/conf.py
pyTooling_ExternalLink_Icons = "all"
pyTooling_ExternalLink_NewTab = False # links open in the same tab
pyTooling_ExternalLink_Hosts = {
"gitlab.example.org": "gitlab", # a GitLab instance of one's own
"pypi.org": "external", # no Python logo for the package index
}
pyTooling_ExternalLink_DownloadSizes = True
pyTooling_ExternalLink_RemoteSizes = [
"https://gitlab.example.org/*/-/releases/*",
]
pyTooling_ExternalLink_RemoteSizeTimeout = 5.0
A malformed value stops the build:
.. code-block:: text
conf.py: pyTooling_ExternalLink_Icons: 'some' is not a selection of links.
Allowed values: none, explicit, all.
conf.py: pyTooling_ExternalLink_RemoteSizeTimeout: Value is not a positive, finite number.
Got value '0'.
.. _LINK/External/Styling:
Styling
*******
The stylesheet draws an icon from CSS custom properties, so a project changes them without replacing the stylesheet -
in a stylesheet of its own, listed in ``html_css_files``:
.. list-table::
:header-rows: 1
:widths: 40 25 35
* - Property
- Default
- Sets
* - ``--pyTooling-link-icon-size``
- ``0.8em``
- the icon's width and height
* - ``--pyTooling-link-icon-gap``
- ``0.2em``
- the gap between the link's text and the icon
* - ``--pyTooling-link-icon-scale``
- ``1``; ``1.15`` for ``download``
- a factor of an icon's size, set on the link's class, e.g. ``a.pytooling-link-icon-download``
* - ``--pyTooling-link-icon-``
- an SVG image
- the icon's shape, e.g. ``--pyTooling-link-icon-github``
The size behind a download is a ````, which a project's stylesheet may style, e.g.
in a smaller font.
An icon is a mask filled with the link's colour, so only the shape of its image counts:
.. code-block:: CSS
:root {
--pyTooling-link-icon-size: 0.7em;
--pyTooling-link-icon-external: url("data:image/svg+xml,...");
}
.. _LINK/External/Output:
HTML and other formats
**********************
Two post-transforms mark the links in HTML, for every builder of the HTML family:
:class:`~pyTooling.Sphinx.ExternalLink.MarkExternalLinks` before Sphinx resolves the references, and
:class:`~pyTooling.Sphinx.ExternalLink.MarkIntersphinxLinks` after. A marked link gets the CSS classes
``pytooling-external-link`` or ``pytooling-intersphinx-link``, and ``pytooling-link-icon-`` naming its icon:
.. code-block:: HTML
pyTooling/pyTooling.Sphinx
Two more post-transforms work on the downloads, in HTML too: :class:`~pyTooling.Sphinx.ExternalLink.MarkDownloadLinks`
marks them with the CSS class ``pytooling-download-link`` and the download or archive icon, and
:class:`~pyTooling.Sphinx.ExternalLink.AddDownloadSizes` writes their sizes.
* The icon is drawn by the link's ``::after`` pseudo-element, so it stays on the line of the link's last word.
* A screen reader reads the icon's alternative text instead, e.g. *(external link, opens in a new tab)* or
*(e-mail address)*.
* In a forced-colours mode, e.g. Windows' high contrast themes, the icon keeps the link's colour.
* A link to an archive also gets the CSS class ``pytooling-archive-``, e.g. ``pytooling-archive-tar-gz``;
its alternative text names the format, e.g. *(download, ZIP archive)*.
* sphinx_rtd_theme draws a download glyph of its own before a download link's text. A marked download link shows its
icon behind the text only, like any other link; with ``pyTooling_ExternalLink_Icons`` set to ``none``, the theme's
glyph stays.
Every other format - e.g. LaTeX - writes the links as before, without a mark and without a size: LaTeX writes a
``:download:`` link as its text, without the file, so a PDF offers no file whose size would help.
The brands' icons - ``github`` to ``readthedocs`` - are their marks from `Simple Icons `__
16.35.0, published under CC0 1.0 Universal; the marks are trademarks of their owners. ``external``, ``mail``,
``download`` and ``archive`` are drawn for pyTooling.Sphinx.