Skip to content

Commit

Permalink
Add galois.typing subpackage and simplify type hints
Browse files Browse the repository at this point in the history
Fixes #338
Fixes #262
Fixes #264
  • Loading branch information
mhostetter committed Apr 16, 2022
1 parent fc5cb76 commit 83130e0
Show file tree
Hide file tree
Showing 24 changed files with 690 additions and 472 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.6
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.6
python-version: 3.8
- name: Install documentation dependencies
run: |
python3 -m pip install --upgrade pip
Expand Down
8 changes: 8 additions & 0 deletions docs/api/galois.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ API Reference

.. currentmodule:: galois

Subpackages
-----------

.. autosummary::
:toctree:

typing

Class factory functions
-----------------------

Expand Down
29 changes: 29 additions & 0 deletions docs/api/typing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.. py:module:: galois.typing
Typing
======

The :obj:`galois.typing` subpackage contains `type aliases <https://docs.python.org/3/library/typing.html#type-aliases>`_ for common
types used in the :obj:`galois` library.

.. currentmodule:: galois.typing

Array-related
-------------

.. autosummary::
:toctree:

ElementLike
IterableLike
ArrayLike
ShapeLike
DTypeLike

Polynomial-related
------------------

.. autosummary::
:toctree:

PolyLike
101 changes: 99 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import sys
sys.path.insert(0, os.path.abspath(".."))

# Need to build docs with Python 3.8 or higher for proper typing annotations, including from __future__ import annotations
assert sys.version_info.major == 3 and sys.version_info.minor >= 8

# Assign a build variable to the builtin module that inerts the @set_module decorator. This is done because set_module
# confuses Sphinx when parsing overloaded functions. When not building the documentation, the @set_module("galois")
# decorator works as intended.
Expand Down Expand Up @@ -196,18 +199,27 @@
"special-members": True,
"member-order": "groupwise",
}
autodoc_typehints = "description"
autodoc_typehints = "signature"
autodoc_typehints_description_target = "documented"
autodoc_typehints_format = "short"

autodoc_type_aliases = {
"ElementLike": "~typing.ElementLike",
"IterableLike": "~typing.IterableLike",
"ArrayLike": "~typing.ArrayLike",
"ShapeLike": "~typing.ShapeLike",
"DTypeLike": "~typing.DTypeLike",
"PolyLike": "~typing.PolyLike",
}

autosummary_generate = True
autosummary_generate_overwrite = True
autosummary_imported_members = True

ipython_execlines = ["import math", "import numpy as np", "import galois"]


# -- Functions and setup -----------------------------------------------------
# -- Monkey-patching -----------------------------------------------------

SPECIAL_MEMBERS = [
"__repr__", "__str__", "__int__",
Expand Down Expand Up @@ -245,5 +257,90 @@ def skip_member(app, what, name, obj, skip, options):
return skip


def process_signature(app, what, name, obj, options, signature, return_annotation):
"""
Monkey-patch the autodoc's processing of signatures.
"""
signature = modify_type_hints(signature)
return_annotation = modify_type_hints(return_annotation)

return signature, return_annotation


def modify_type_hints(signature, do_print=False):
"""
Modify the autodoc type hint signatures to be more readable. Union[x, y] is replaced with x | y.
Optional[x] is replaced with x | None. Also, short names are used (and properly linked).
"""
if signature:
# Ensure types from the typing module are properly hyperlinked
for type_name in ["Tuple", "List", "Sequence", "Dict", "Optional", "Union", "Iterator", "Type", "Literal"]:
signature = signature.replace(f"{type_name}[", f"~typing.{type_name}[")
signature = signature.replace("~typing.~typing", "~typing")

# Convert Optional[a] to a | None
idx = 0
while True:
idx = signature.find("~typing.Optional[", idx)
if idx == -1:
break
end_idx = signature.find("] = None", idx)
pre = signature[:idx]
post = signature[end_idx + 1:]
optional_str = signature[idx:end_idx + 1]
optional_str = optional_str[len("~typing.Optional[") : -len("]")]
optional_str += " | None"
signature = pre + optional_str + post
idx = len(pre) + len(optional_str)

# Convert Union[a, b] to a | b
idx = 0
while True:
idx = signature.find("~typing.Union[", idx)
if idx == -1:
break
end_idx = signature.find("]", idx)
pre = signature[:idx]
post = signature[end_idx + 1:]
union_str = signature[idx:end_idx + 1]
union_str = union_str[len("~typing.Union[") : -len("]")]
union_str = union_str.replace(", ", " | ")
signature = pre + union_str + post
idx = len(pre) + len(union_str)

# Ensure types from the galois.typing subpackage are properly hyperlinked
for type_name in autodoc_type_aliases.keys():
signature = signature.replace(type_name, autodoc_type_aliases[type_name])
signature = signature.replace("~typing.~typing", "~typing")

# Ensure forward references are properly linked by removing '' and adding ~
for type_name in ["Array", "FieldArray", "Poly"]:
signature = signature.replace(f"'{type_name}'", f"~{type_name}")

# Sometimes fully qualified names are in the signature. Convert them to short names. For example, ~galois._fields._gf2.GF2
# is used instead of ~galois.GF2.
idx = 0
while True:
idx = signature.find("~galois._", idx)
if idx == -1:
break
end_idx = signature.find(" ", idx)
if end_idx == -1:
end_idx = len(signature)
pre = signature[:idx]
post = signature[end_idx + 1:]
fullref_str = signature[idx:end_idx + 1]
fullref_str = "~galois." + fullref_str.split(".")[-1]
signature = pre + fullref_str + post
idx = len(pre) + len(fullref_str)

# Ensure NumPy references link properly
signature = signature.replace("np.ndarray", "~numpy.ndarray")
signature = signature.replace("np.random", "~numpy.random")

return signature


def setup(app):
app.connect("autodoc-skip-member", skip_member)
app.connect("autodoc-process-signature", process_signature)
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ APA
api/number-theory.rst
api/integer-factorization.rst
api/primes.rst
api/typing.rst

.. toctree::
:caption: Release Notes
Expand Down
5 changes: 4 additions & 1 deletion galois/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from ._version import __version__

# Subpackages
# Nested modules
from ._polys import * # Needs to be imported before _fields
from ._fields import *
###############################################################################
Expand All @@ -26,3 +26,6 @@
from ._ntt import *
from ._polymorphic import *
from ._prime import *

# Subpackages
from . import typing
Loading

0 comments on commit 83130e0

Please sign in to comment.