Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 129 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

import builtins
import datetime
import enum
import importlib
import logging
import os
import pkgutil
from typing import TYPE_CHECKING

from sphinx import addnodes

# NB: a private module of ``sphinx-autodoc-typehints``, used deliberately -- see
# ``bind_type_checking_names`` below for what it buys and why reimplementing it
# here would be worse. If a future release moves it the import fails loudly at
Expand All @@ -28,7 +32,8 @@
import pcapkit

if TYPE_CHECKING:
from typing import Any, Dict, Optional
from typing import Any, Dict, List, Optional
from docutils.nodes import document
from sphinx.application import Sphinx

logger = logging.getLogger('pcapkit-sphinx')
Expand Down Expand Up @@ -393,6 +398,124 @@ def claim_attribute_signature(app: 'Sphinx', what: str, name: str, # pylint: di
return None


#: Every name :mod:`builtins` binds -- ``dict``, ``set``, ``type`` and the rest.
BUILTIN_NAMES = frozenset(dir(builtins))


def resolve_builtins_exactly(app: 'Sphinx', doctree: 'document') -> None: # pylint: disable=unused-argument
"""Stop a bare builtin name in a type being suffix-matched to a pcapkit member.

The Python domain resolves a reference carrying a ``refspecific`` attribute
by *fuzzy* search when no exact match exists: any documented object whose
name ends in ``.dict`` will do. Every type it builds implicitly carries that
attribute -- the doc-field types (``:type:``, ``:vartype:``) set it to
:data:`True`, and an annotation sets it to :data:`False`, which counts all
the same, because the domain only asks whether the attribute is *present*.
So ``dict`` in an annotation found the registry members
:attr:`TCP.dict <pcapkit.const.reg.apptype.tcp.TCP.dict>` and
``UDP.dict`` and linked to the first, with a ``more than one target found``
warning; ``type`` and ``set`` went the same way. A registry growing one
member named like a builtin would mislink it *silently*.

Dropping the attribute for a builtin name leaves only the exact lookup, by
the bare name and within the current module and class. That finds nothing
for ``dict``, so :mod:`~sphinx.ext.intersphinx` links it to the Python docs.

An explicit role is left alone: ``:class:`.dict``` asks for the fuzzy
search by its leading dot, and only a role's node carries ``refwarn``.

Args:
app: Sphinx application.
doctree: The document just read.

"""
for node in doctree.findall(addnodes.pending_xref):
attributes = node.attributes
if (attributes.get('refdomain') == 'py' and 'refspecific' in attributes
and 'refwarn' not in attributes
and attributes.get('reftarget') in BUILTIN_NAMES):
del attributes['refspecific']


#: Enum member docstrings as autodoc read them, held from
#: :func:`save_enum_member_docstring` until :func:`restore_enum_member_docstring`.
_ENUM_MEMBER_DOCSTRINGS = {} # type: Dict[str, List[str]]


def is_enum_member(name: str) -> bool:
"""Whether ``name`` is the fully qualified name of an enum member.

Decided from the name, because autodoc does not hand over the member: for
``PayloadProtocolIdentifier.H_323`` Sphinx 9.1 passes the plain ``int`` 13.

Args:
name: Fully qualified name of the object being documented.

Returns:
:data:`True` if the name before the last dot is an enumeration and the
last part is one of its members.

"""
parent_name, _, member = name.rpartition('.')
try:
parent = pkgutil.resolve_name(parent_name)
except (ImportError, AttributeError, ValueError):
return False
return isinstance(parent, enum.EnumMeta) and member in parent.__members__


def save_enum_member_docstring(app: 'Sphinx', what: str, name: str, # pylint: disable=unused-argument
obj: 'Any', options: 'Any', lines: 'List[str]') -> None: # pylint: disable=unused-argument
"""Keep an enum member's docstring before :mod:`~sphinx.ext.napoleon` reads it.

Napoleon takes the first line of every attribute docstring as Google's
inline ``type: description`` form and splits it at the first colon. A
registry member's docstring is the registry's own text, so the colon is
usually a URL's: ``H.323 [http://standard.pictel.com/...]`` rendered as
*Type:* ``H.323 [http`` above the description ``//standard.pictel.com/...``,
on 51 members. The bogus type was also parsed as a cross-reference, which is
where the ``http`` and ``Echo`` ``more than one target found`` warnings came
from.

Args:
app: Sphinx application.
what: Type of the object being documented.
name: Fully qualified name of the object.
obj: The object itself.
options: Directive options.
lines: The docstring, which is not changed here.

"""
if what == 'attribute' and is_enum_member(name):
_ENUM_MEMBER_DOCSTRINGS[name] = list(lines)


def restore_enum_member_docstring(app: 'Sphinx', what: str, name: str, # pylint: disable=unused-argument
obj: 'Any', options: 'Any', lines: 'List[str]') -> None: # pylint: disable=unused-argument
"""Undo napoleon's inline type on an enum member, from the saved docstring.

An enum member's type is its enumeration, which autodoc already shows, so a
``:type:`` field napoleon added there is always a misread line. Only that
case is undone: a docstring napoleon rewrote in any other way keeps the
rewrite.

Args:
app: Sphinx application.
what: Type of the object being documented.
name: Fully qualified name of the object.
obj: The object itself.
options: Directive options.
lines: The docstring after napoleon, restored in place.

"""
original = _ENUM_MEMBER_DOCSTRINGS.pop(name, None)
if original is None:
return
if (any(line.startswith(':type:') for line in lines)
and not any(line.startswith(':type:') for line in original)):
lines[:] = original


# NB: four handlers used to sit here -- ``remove_module_docstring``,
# ``process_docstring``, ``process_fields`` and ``source_read`` -- each with its
# ``app.connect`` call commented out in ``setup`` below, so none of them had run
Expand Down Expand Up @@ -422,3 +545,8 @@ def setup(app: 'Sphinx') -> None:
# would otherwise win the tie on registration order -- conf.py's ``setup`` runs
# after the extensions in ``extensions`` have been set up.
app.connect('autodoc-process-signature', claim_attribute_signature, priority=400)
app.connect('doctree-read', resolve_builtins_exactly)
# NB: either side of napoleon's ``autodoc-process-docstring`` handler, which
# connects at the default 500.
app.connect('autodoc-process-docstring', save_enum_member_docstring, priority=400)
app.connect('autodoc-process-docstring', restore_enum_member_docstring, priority=600)
2 changes: 1 addition & 1 deletion docs/source/pcapkit/corekit/context.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ Decoding an ESP tunnel end to end:
Type Variables
--------------

.. data:: pcapkit.corekit.context._CT
.. data:: _CT
:type: pcapkit.corekit.context.ProtocolContext
6 changes: 3 additions & 3 deletions docs/source/pcapkit/corekit/decorators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ decorators, including :func:`~pcapkit.corekit.decorators.seekset`,
Type Variables
--------------

.. data:: pcapkit.corekit.decorators.R_seekset
.. data:: R_seekset
:type: typing.Any

.. data:: pcapkit.corekit.decorators.R_beholder
.. data:: R_beholder
:type: pcapkit.protocols.protocol.ProtocolBase

.. data:: pcapkit.corekit.decorators.R_prepare
.. data:: R_prepare
:type: pcapkit.protocols.schema.schema.Schema
4 changes: 2 additions & 2 deletions docs/source/pcapkit/corekit/fields/collections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Option List Fields
Type Variables
~~~~~~~~~~~~~~

.. data:: pcapkit.corekit.fields.collections._TL
.. data:: _TL
:type: pcapkit.protocols.schema.schema.Schema | pcapkit.corekit.fields.field.FieldBase | bytes

.. data:: pcapkit.corekit.fields.collections._TS
.. data:: _TS
:type: pcapkit.protocols.schema.schema.Schema
2 changes: 1 addition & 1 deletion docs/source/pcapkit/corekit/fields/field.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ Internal Definitions
Type Variables
--------------

.. data:: pcapkit.corekit.fields.field._T
.. data:: _T
:type: typing.Any
6 changes: 3 additions & 3 deletions docs/source/pcapkit/corekit/fields/ipaddress.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ Internal Definitions
Type Variables
~~~~~~~~~~~~~~

.. data:: pcapkit.corekit.fields.ipaddress._T
.. data:: _T
:type: ipaddress.IPv4Address | ipaddress.IPv6Address | ipaddress.IPv4Interface | ipaddress.IPv6Interface

.. data:: pcapkit.corekit.fields.ipaddress._AT
.. data:: _AT
:type: ipaddress.IPv4Address | ipaddress.IPv6Address

.. data:: pcapkit.corekit.fields.ipaddress._IT
.. data:: _IT
:type: ipaddress.IPv4Interface | ipaddress.IPv6Interface
8 changes: 4 additions & 4 deletions docs/source/pcapkit/corekit/fields/misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@ Generic Data Fields
Type Variables
~~~~~~~~~~~~~~

.. data:: pcapkit.corekit.fields.misc._TC
.. data:: _TC
:type: typing.Any

.. data:: pcapkit.corekit.fields.misc._TS
.. data:: _TS
:type: pcapkit.protocols.schema.schema.Schema

.. data:: pcapkit.corekit.fields.misc._TP
.. data:: _TP
:type: pcapkit.protocols.protocol.Protocol

.. data:: pcapkit.corekit.fields.misc._TN
.. data:: _TN
:type: pcapkit.corekit.sentinels.NoValueType
2 changes: 1 addition & 1 deletion docs/source/pcapkit/corekit/fields/numbers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,5 @@ Internal Definitions
Type Variables
--------------

.. data:: pcapkit.corekit.fields.numbers._T
.. data:: _T
:type: int
2 changes: 1 addition & 1 deletion docs/source/pcapkit/corekit/fields/strings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ Internal Definitions
Type Variables
--------------

.. data:: pcapkit.corekit.fields.strings._T
.. data:: _T
:type: str | bytes | python:dict[str, typing.Any]
6 changes: 3 additions & 3 deletions docs/source/pcapkit/corekit/infoclass.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ Internal Definitions
Type Variables
--------------

.. data:: pcapkit.corekit.infoclass.KT
.. data:: KT
:type: typing.Any

.. data:: pcapkit.corekit.infoclass.VT
.. data:: VT
:type: typing.Any

.. data:: pcapkit.corekit.infoclass.ST
.. data:: ST
:type: typing.Type[pcapkit.corekit.infoclass.Info]
2 changes: 1 addition & 1 deletion docs/source/pcapkit/corekit/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ with a class name.
Type Variables
--------------

.. data:: pcapkit.corekit.module._T
.. data:: _T
:type: typing.Any
6 changes: 3 additions & 3 deletions docs/source/pcapkit/corekit/multidict.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ values per key, based on the `Werkzeug`_ project.
Type Variables
--------------

.. data:: pcapkit.corekit.multidict._T
.. data:: _T
:type: typing.Any

.. data:: pcapkit.corekit.multidict._KT
.. data:: _KT
:type: typing.Any

.. data:: pcapkit.corekit.multidict._VT
.. data:: _VT
:type: typing.Any
2 changes: 1 addition & 1 deletion docs/source/pcapkit/foundation/engines/engine.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,5 @@ Internal Definitions
Type Variables
--------------

.. data:: pcapkit.foundation.engines.engine._T
.. data:: _T
:type: typing.Any
2 changes: 1 addition & 1 deletion docs/source/pcapkit/foundation/extraction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ extracts parameters from a PCAP file.
Type Variables
--------------

.. data:: pcapkit.foundation.extraction._P
.. data:: _P
:type: typing.Any

.. |pypcap| replace:: ``pypcap``
Expand Down
8 changes: 5 additions & 3 deletions docs/source/pcapkit/foundation/reassembly/ip/ip.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ Data Models
:members:
:show-inheritance:

.. data:: pcapkit.foundation.reassembly.data.ip.BufferID
:type: typing.Tuple[_AT, _AT, int, pcapkit.const.reg.transtype.TransType]
.. data:: BufferID
:module: pcapkit.foundation.reassembly.data.ip
:type: typing.Tuple[~pcapkit.foundation.reassembly.data.ip._AT, ~pcapkit.foundation.reassembly.data.ip._AT, int, pcapkit.const.reg.transtype.TransType]

Data module for buffer ID.

Expand All @@ -45,5 +46,6 @@ Data Models
Type Variables
--------------

.. data:: pcapkit.foundation.reassembly.data.ip._AT
.. data:: _AT
:module: pcapkit.foundation.reassembly.data.ip
:type: ipaddress.IPv4Address | ipaddress.IPv6Address
8 changes: 4 additions & 4 deletions docs/source/pcapkit/foundation/reassembly/reassembly.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,22 @@ Internal Definitions
Type Variables
--------------

.. data:: pcapkit.foundation.reassembly.reassembly._PT
.. data:: _PT
:type: pcapkit.corekit.infoclass.Info

Packet data structure.

.. data:: pcapkit.foundation.reassembly.reassembly._DT
.. data:: _DT
:type: pcapkit.corekit.infoclass.Info

Datagram data structure.

.. data:: pcapkit.foundation.reassembly.reassembly._IT
.. data:: _IT
:type: tuple

Buffer ID data structure.

.. data:: pcapkit.foundation.reassembly.reassembly._BT
.. data:: _BT
:type: pcapkit.corekit.infoclass.Info

Buffer data structure.
8 changes: 5 additions & 3 deletions docs/source/pcapkit/foundation/reassembly/tcp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,13 @@ Data Models
Type Variables
==============

.. data:: pcapkit.foundation.reassembly.data.tcp._AT
.. data:: _AT
:module: pcapkit.foundation.reassembly.data.tcp
:type: ipaddress.IPv4Address | ipaddress.IPv6Address

.. data:: pcapkit.foundation.reassembly.data.tcp.BufferID
:type: typing.Tuple[_AT, int, _AT, int]
.. data:: BufferID
:module: pcapkit.foundation.reassembly.data.tcp
:type: typing.Tuple[~pcapkit.foundation.reassembly.data.tcp._AT, int, ~pcapkit.foundation.reassembly.data.tcp._AT, int]
:no-index:

Buffer ID data structure.
3 changes: 2 additions & 1 deletion docs/source/pcapkit/foundation/traceflow/tcp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,5 +170,6 @@ Data Structures
Type Variables
--------------

.. data:: pcapkit.foundation.traceflow.data.tcp._AT
.. data:: _AT
:module: pcapkit.foundation.traceflow.data.tcp
:type: ipaddress.IPv4Address | ipaddress.IPv6Address
3 changes: 2 additions & 1 deletion docs/source/pcapkit/protocols/application/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function rather than encapsulation, see
Protocol Registry
-----------------

.. data:: pcapkit.protocols.application.APPTYPE
.. data:: APPTYPE
:module: pcapkit.protocols.application

alias of :class:`pcapkit.const.reg.apptype.apptype.AppType`
Loading
Loading