From 55e9673a92751b2534ec2f59455fa09967780948 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Wed, 9 Jul 2025 13:51:05 -0700 Subject: [PATCH 01/10] fix(tox): Use envpython key out of an abundance of caution. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 2b170b3..85abdd9 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ commands = description = Run Unit Tests commands_pre = {envpython} --version - python -c 'import pathlib; pathlib.Path("{env_site_packages_dir}/cov.pth").write_text("import coverage; coverage.process_startup()")' + {envpython} -c 'import pathlib; pathlib.Path("{env_site_packages_dir}/cov.pth").write_text("import coverage; coverage.process_startup()")' [testenv:coverage] description = Report Code Coverage From 96d9b1b98980abe19fedb32ced8f937d2e96aa16 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Wed, 9 Jul 2025 14:12:41 -0700 Subject: [PATCH 02/10] feat(pre-commit): Update to latest version of pre-commit, and include addition check-toml hook. --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8723e5..eb3b3bf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,13 +3,14 @@ ci: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v5.0.0 hooks: - id: trailing-whitespace exclude: ^tests/(integration|unit)/data/ - id: end-of-file-fixer exclude: ^(tests/(integration|unit)/data/|LICENSE|.python-version-default) - id: check-yaml + - id: check-toml - id: check-added-large-files - id: requirements-txt-fixer From 529d523fd4dfcc7b76ecde2f21f74c7a78956cc4 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Wed, 9 Jul 2025 14:13:34 -0700 Subject: [PATCH 03/10] chore(tox): trim whitespace linting. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 85abdd9..93f3f78 100644 --- a/tox.ini +++ b/tox.ini @@ -46,7 +46,7 @@ commands = changedir = docs extras = doc allowlist_externals = rm -commands = +commands = sphinx-build -W -b html -d {envtmpdir}/doctrees source {envtmpdir}/html commands_post = rm -rf {envtmpdir} From 8251d0f07ddc07fbaf7e2dc6844fdc70a21284e2 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 06:21:41 -0700 Subject: [PATCH 04/10] feat(pyproject): Improve default pylint rules due to various issues and preferences. --- pyproject.toml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1811b0f..85420c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,8 +86,18 @@ module-naming-style = "any" [tool.pylint.format] max-line-length = 88 -# [tool.pylint."messages control"] -# disable = [] +[tool.pylint."messages control"] +disable = [ + "R1731", # consider-using-max-builtin +] + +[tool.pylint."*.pyi"] +# https://github.com/pylint-dev/pylint/issues/9096 +# https://github.com/pylint-dev/pylint/issues/9417 +disable = [ + "W0613", # unused-argument + "W0231", # super-init-not-called +] [tool.ruff] line-length = 88 From 48ea9c526da1c5dbec26bf974a88b7f0500fa64a Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 14:41:10 -0700 Subject: [PATCH 05/10] feat(docs.ext): Implement custom VS code dark+ inspired syntax highlighting theme as a pygments style. --- docs/source/_ext/styles.py | 145 +++++++++++++++++++++++++++++++++++++ docs/source/_ext/utils.py | 57 +++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 docs/source/_ext/styles.py create mode 100644 docs/source/_ext/utils.py diff --git a/docs/source/_ext/styles.py b/docs/source/_ext/styles.py new file mode 100644 index 0000000..1b66028 --- /dev/null +++ b/docs/source/_ext/styles.py @@ -0,0 +1,145 @@ +# BSD 3-Clause License +# +# Copyright (c) 2025, Spill-Tea +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Custom Pygment syntax highlighting style.""" + +from typing import ClassVar + +from pygments.style import Style +from pygments.token import ( + Comment, + Error, + Keyword, + Name, + Number, + Operator, + Other, + Punctuation, + String, + Text, + _TokenType, +) +from utils import get_brackets + + +def bold(color: str) -> str: + """Embolden color.""" + return f"bold {color}" + + +def italic(color: str) -> str: + """Italicize color.""" + return f"italic {color}" + + +def underline(color: str) -> str: + """Underline text with color.""" + return f"underline {color}" + + +class Colors: + """Define colors used more than once.""" + + datatype: str = "#61C8B0" + variable: str = "#9CDCFE" + function: str = "#DCDCAA" + reserved: str = "#639BD4" + default: str = "#D4D4D4" + control: str = "#C586C0" + builtin: str = "#4EC9B0" + declare: str = "#569CD6" + problem: str = "#C3726A" + comment: str = "#6A9955" + bracket: str = "#F9C922" + + +class VSCodeDarkPlus(Style): + """Custom theme deeply inspired by VSCode Dark+ as a pygments style.""" + + background_color: str = "#1E1E1E" + + styles: ClassVar[dict[_TokenType, str]] = { # pyright: ignore + # Comments + Comment: Colors.comment, + Comment.Single: Colors.comment, + Comment.Preproc: Colors.reserved, + Comment.Special: bold(Colors.declare), + Comment.Hashbang: italic("#7C7046"), + Comment.Multiline: italic("#525252"), + # Keywords + Keyword: Colors.control, + Keyword.Type: Colors.datatype, + Keyword.Declare: bold(Colors.declare), + Keyword.Constant: bold(Colors.declare), + Keyword.Reserved: bold(Colors.reserved), + Keyword.Namespace: Colors.control, + # Variable Names + Name: Colors.variable, + Name.Type: Colors.builtin, + Name.Class: bold(Colors.datatype), + Name.Builtin: Colors.builtin, + Name.Builtin.Pseudo: italic(Colors.variable), + Name.Constant: "#4FC1FF", + Name.Function: Colors.function, + Name.Function.Magic: italic(Colors.function), + Name.Variable: Colors.variable, + Name.Variable.Class: Colors.datatype, + Name.Variable.Magic: Colors.function, + Name.Namespace: Colors.datatype, + Name.Exception: Colors.problem, + # (Doc)Strings + Text: Colors.default, + String: "#C9937A", + String.Doc: italic(Colors.comment), + String.Doc.Title: bold("#80AE6B"), + String.Affix: Colors.declare, + String.Regex: "#D16969", + String.Escape: "#D7BA7D", + String.Interpol: Colors.declare, + # Numbers + Number: "#B6CEA9", + Number.Other: Colors.declare, + # Operators + Operator: Colors.default, + Operator.Word: Colors.control, + # Punctuation + Punctuation: Colors.default, + **get_brackets( + [ + Colors.bracket, + "#EA2EEA", + "#5DCD4C", + "#3B9ADE", + ] + ), + Punctuation.Error: underline("#F92222"), + # Miscellaneous + Error: underline(bold(Colors.problem)), + Other: Colors.default, + } diff --git a/docs/source/_ext/utils.py b/docs/source/_ext/utils.py new file mode 100644 index 0000000..65f3688 --- /dev/null +++ b/docs/source/_ext/utils.py @@ -0,0 +1,57 @@ +# BSD 3-Clause License +# +# Copyright (c) 2025, Spill-Tea +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Common shared utility functions.""" + +from typing import Iterator + +from pygments.token import _TokenType, string_to_tokentype + + +def get_bracket_level(n: int) -> _TokenType: + """Retrieve the bracket depth level token.""" + name: str = f"Punctuation.Level{n}" + + return string_to_tokentype(name) + + +def nbrackets(n: int) -> Iterator[_TokenType]: + """Dynamically generate tokentype to identify a variable number of brackets.""" + for j in range(n): + yield get_bracket_level(j) + + +def dynamic_brackets(colors: list[str]) -> list[tuple[_TokenType, str]]: + """Dynamically generate bracket color options from a list of colors.""" + return [(key, colors[idx]) for idx, key in enumerate(nbrackets(len(colors)))] + + +def get_brackets(colors: list[str]) -> dict[_TokenType, str]: + """Get brackets in dictionary form.""" + return dict(dynamic_brackets(colors)) From a2380f66a923b87ac10c4a0d2a319b898f0fc934 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 14:42:34 -0700 Subject: [PATCH 06/10] feat(docs._ext): Implement custom Python Lexer extension to tokenize additional language features, including a primitive rainbow bracket colorizer. --- docs/source/_ext/lexers.py | 209 +++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 docs/source/_ext/lexers.py diff --git a/docs/source/_ext/lexers.py b/docs/source/_ext/lexers.py new file mode 100644 index 0000000..41e8554 --- /dev/null +++ b/docs/source/_ext/lexers.py @@ -0,0 +1,209 @@ +# BSD 3-Clause License +# +# Copyright (c) 2025, Spill-Tea +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Customized python lexer.""" + +from collections import deque +from collections.abc import Iterator +from typing import ClassVar + +from pygments.lexer import bygroups, include +from pygments.lexers.python import PythonLexer +from pygments.token import ( + Comment, + Keyword, + Name, + Number, + Punctuation, + String, + Text, + Whitespace, + _TokenType, +) +from utils import get_bracket_level + + +def _find(it, obj, key=lambda a, b: a == b) -> int: + for n, j in enumerate(it): + if key(j, obj): + return n + raise IndexError("Unable to find object.") + + +def _get_index(n: int): + def inner(a, b) -> bool: + return a[n] == b + + return inner + + +root: list = [ + (r"\n", Whitespace), + ( # single line docstrings (edge case) + r'^(\s*)([rRuUbB]{,2})("""(?:.)*?""")', + bygroups(Whitespace, String.Affix, String.Doc), + ), + ( # Modfied triple double quote docstrings to highlight docstring titles + r'^(\s*)([rRuUbB]{,2})(""")', + bygroups(Whitespace, String.Affix, String.Doc), + "docstring-double", + ), + ( # Intentionally treat text encapsulated within single triple quotes as String + r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')", + bygroups(Whitespace, String.Affix, String), + ), + (r"\A#!.+$", Comment.Hashbang), + ( + # Format Special Common Keyword Comments + # NOTE: Must come before Comment.Single token in order to be matched. + r"(#\s*)(TODO|FIXME|NOTE|BUG|HACK|XXX)(:?)(.*$)", + bygroups(Comment.Single, Comment.Special, Comment.Special, Comment.Single), + ), + (r"#.*$", Comment.Single), + (r"\\\n", Text), + (r"\\", Text), + include("keywords"), + include("soft-keywords"), + ( + r"(def)((?:\s|\\\s)+)", + bygroups(Keyword.Declare, Whitespace), + "funcname", + ), + ( + r"(class)((?:\s|\\\s)+)", + bygroups(Keyword.Declare, Whitespace), + "classname", + ), + ( + r"(from)((?:\s|\\\s)+)", + bygroups(Keyword.Namespace, Whitespace), + "fromimport", + ), + ( + r"(import)((?:\s|\\\s)+)", + bygroups(Keyword.Namespace, Whitespace), + "import", + ), + include("expr"), +] + + +python_tokens: dict[str, list] = PythonLexer.tokens.copy() +python_tokens["root"] = root +python_tokens["docstring-double"] = [ + ( + r"(?<=\n)(\s*)(Args|Attributes|Returns|Raises|" + r"Examples|Yields|References|Notes|Equations)(:)(\s*)", + bygroups(Whitespace, String.Doc.Title, String.Doc, Whitespace), + ), + (r'^\s*(?:""")', String.Doc, "#pop"), + (r".+[\r\n]*", String.Doc), +] + +# Tokenize function names when used (i.e. function calls) +# NOTE: Must be inserted before general `Name` token but after `Name.Builtins` token +# NOTE: Implementation limitations -> we cannot distinguish between class and function +# calls using regex based parsing alone (i.e without semantic analysis). +python_tokens["name"].insert( + _find(python_tokens["name"], Name, _get_index(1)), + (r"\b([a-zA-Z_]\w*)(?=\s*\()", Name.Function), +) + +python_tokens["numbers"] = [ + ( + r"(\d(?:_?\d)*\.(?:\d(?:_?\d)*)?|(?:\d(?:_?\d)*)?\.\d(?:_?\d)*)" + r"([eE][+-]?\d(?:_?\d)*)?([jJ]?)", + bygroups(Number.Float, Number.Float, Number.Other), + ), + (r"(\d(?:_?\d)*[eE][+-]?\d(?:_?\d)*)([jJ]?)", bygroups(Number.Float, Number.Other)), + (r"(0[oO])((?:_?[0-7])+)", bygroups(Number.Other, Number.Oct)), + (r"(0[bB])((?:_?[01])+)", bygroups(Number.Other, Number.Bin)), + (r"(0[xX])((?:_?[a-fA-F0-9])+)", bygroups(Number.Other, Number.Hex)), + (r"(\d(?:_?\d)*)([jJ]?)", bygroups(Number.Integer, Number.Other)), +] + + +class CustomPythonLexer(PythonLexer): + """Enhanced regex-based python Lexer. + + Notes: + 1. Implemented a simple stack based rainbow bracket colorizer. + * limitation: Only detects errors that close more brackets than opens. + 2. Highlight Docstring titles (assumes google docstring format) + 3. Improved highlighting function calls (with limitations) + 4. Modify display of number components which indicate a different base number. + + """ + + n_brackets: int + _stack: deque[int] + tokens: ClassVar[dict[str, list]] = python_tokens + + def __init__(self, **options) -> None: + super().__init__(**options) + self._stack = deque[int]() + self.n_brackets = int(options.get("n_brackets", 3)) + + def _enter(self) -> _TokenType: + """Retrieve next token in cycle.""" + idx = len(self._stack) % self.n_brackets + self._stack.append(idx) + + return get_bracket_level(idx) + + def _exit(self) -> _TokenType: + """Remove element from stack and return token.""" + try: + idx: int = self._stack.pop() + return get_bracket_level(idx) + + except IndexError: + return Punctuation.Error + + def get_tokens_unprocessed( + self, + text, + stack=("root",), + ) -> Iterator[tuple[int, _TokenType, str]]: + _token: _TokenType + for idx, token, value in super().get_tokens_unprocessed(text, stack): + _token = token + if token is Name and value.isupper(): + _token = Name.Constant + + elif token is Punctuation: + match value: + case "(" | "[" | "{": + _token = self._enter() + case "}" | "]" | ")": + _token = self._exit() + case _: + ... + + yield idx, _token, value From 1073b45c08339adb7325f6e070e83dc1afef0885 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 14:44:07 -0700 Subject: [PATCH 07/10] feat(docs.conf): Update doc config to setup custom extensions, and include additional flags to trigger other builtin sphinx extensions. --- docs/source/_static/custom.css | 4 ++- docs/source/conf.py | 62 +++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index 10930d7..e0c7cf9 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -1,4 +1,6 @@ -@import url("https://fonts.googleapis.com/css?family=Space Grotesk:wght@400;700&display=swap"); +@import url( + "https://fonts.googleapis.com/css?family=Space Grotesk:wght@400;700&display=swap" +); body { font-family: 'Space Grotesk', sans-serif; diff --git a/docs/source/conf.py b/docs/source/conf.py index 1387eae..e61cec9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,25 +1,55 @@ -# Configuration file for the Sphinx documentation builder. +# BSD 3-Clause License # -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html +# Copyright (c) 2025, Spill-Tea +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Sphinx configuration file.""" -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information import os import sys +from sphinx.application import Sphinx +from sphinx.highlighting import lexer_classes as _lexer_registry -sys.path.insert(0, os.path.abspath("../src/")) +sys.path.insert(0, os.path.abspath("../src/")) # Required to see python package +sys.path.append(os.path.abspath("./_ext")) # Required for custom extensions + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "PyTemplate" copyright = "2025, Jason C Del Rio (Spill-Tea)" author = "Jason C Del Rio (Spill-Tea)" release = "v0.0.1" + # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - extensions = [ "sphinx.ext.duration", "sphinx.ext.viewcode", @@ -28,13 +58,29 @@ "sphinx.ext.napoleon", ] +napoleon_google_docstring = True # Use google docstring format (sphinx.ext.napoleon) +autosummary_generate = True # Turn on sphinx.ext.autosummary + templates_path = ["_templates"] exclude_patterns = [] +pygments_style = "styles.VSCodeDarkPlus" # Use custom syntax highlighting (style) + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - html_theme = "sphinx_rtd_theme" html_static_path = ["_static"] html_css_files = ["custom.css"] + + +def setup(app: Sphinx) -> None: + """Custom sphinx application startup setup.""" + from lexers import CustomPythonLexer # type: ignore + + # NOTE: overwrite default python lexer + app.add_lexer("python", CustomPythonLexer) + assert "python" in _lexer_registry, "python language not found in registry" + assert _lexer_registry["python"] == CustomPythonLexer, ( + "custom Lexer not found in registry." + ) From 63b158d5e8d435aeebeb1835cfd95148c7346081 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 15:31:51 -0700 Subject: [PATCH 08/10] fix(_ext.lexers): Update default bracket colors to 4. --- docs/source/_ext/lexers.py | 2 +- docs/source/api.rst | 5 ----- docs/source/api/index.rst | 13 +++++++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) delete mode 100644 docs/source/api.rst create mode 100644 docs/source/api/index.rst diff --git a/docs/source/_ext/lexers.py b/docs/source/_ext/lexers.py index 41e8554..9421ff3 100644 --- a/docs/source/_ext/lexers.py +++ b/docs/source/_ext/lexers.py @@ -168,7 +168,7 @@ class CustomPythonLexer(PythonLexer): def __init__(self, **options) -> None: super().__init__(**options) self._stack = deque[int]() - self.n_brackets = int(options.get("n_brackets", 3)) + self.n_brackets = int(options.get("n_brackets", 4)) def _enter(self) -> _TokenType: """Retrieve next token in cycle.""" diff --git a/docs/source/api.rst b/docs/source/api.rst deleted file mode 100644 index b8bc495..0000000 --- a/docs/source/api.rst +++ /dev/null @@ -1,5 +0,0 @@ -API -=== - -.. autosummary:: - :toctree: generated diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst new file mode 100644 index 0000000..415beaa --- /dev/null +++ b/docs/source/api/index.rst @@ -0,0 +1,13 @@ +PyTemplate API Documentation +============================ + +PyTemplate API documentation. + + +.. toctree:: + :caption: Submodules + + +.. autosummary:: + :toctree: generated + :recursive: From 8733cc6e6b90caea1443447373804f3b288c3e97 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 15:37:04 -0700 Subject: [PATCH 09/10] docs(api): Provide example api documentation with example code block to highlight additional features provided through custom lexer and style. --- docs/source/api/index.rst | 48 +++++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 22 ++++-------------- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index 415beaa..1af17b1 100644 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -3,6 +3,54 @@ PyTemplate API Documentation PyTemplate API documentation. +.. code-block:: python + :caption: example.py + + from typing import ClassVar as ClassV + + CONSTANT_A: int = 0xFF + CONSTANT_B: float = np.pi + + # NOTE: this is an example class + class Example(object): + """Example docstring. + + Args: + arg1 (str): argument 1 + arg2 (int): argument 2 + + Attributes: + data (dict): data + + """ + arg1: str + arg2: int + data: dict + other: ClassV[list[int]] = [1, 5, 7] + + def __init__(self, arg1: str, arg2: int) -> None: + self.arg1 = arg1 + self.arg2 = arg2 + self.data = { + "a": [(1, 2, (3, 4, 5)), (6, 7, (8, 9 , 10))], + "b": {"c": (7, 4, 3), "d": {"e", "f", "g"}}, + } + + def __getattr__(self, value): + return self.data[value] + + def method(self, value): + return self[value] + + def write(self, text): + print(f"{text:<5}\n") + + def do_something(self, value): + if value > CONSTANT_A: + return value - CONSTANT_B + else: + return value + 0b10011 + .. toctree:: :caption: Submodules diff --git a/docs/source/index.rst b/docs/source/index.rst index c3dfcb9..d86f4f0 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,29 +1,17 @@ -.. PyTemplate documentation master file, created by - sphinx-quickstart on Thu Jun 12 22:11:46 2025. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - PyTemplate documentation ======================== -Add your content using ``reStructuredText`` syntax. See the -`reStructuredText `_ -documentation for details. - -.. automodule:: PyTemplate.__init__ - :members: - :undoc-members: - :show-inheritance: +Include text here. .. toctree:: - :maxdepth: 2 - :caption: Contents: + :hidden: - api + Home page + API reference Indices and tables ================== * :ref:`genindex` * :ref:`modindex` -* :ref:`search` \ No newline at end of file +* :ref:`search` From 3829e4b62be3b648dfdcff15575615c31b4c53e4 Mon Sep 17 00:00:00 2001 From: Spill-Tea Date: Thu, 10 Jul 2025 15:43:42 -0700 Subject: [PATCH 10/10] feat(typed): add py.typed file for static typing (mypy) compatibility. --- src/PyTemplate/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/PyTemplate/py.typed diff --git a/src/PyTemplate/py.typed b/src/PyTemplate/py.typed new file mode 100644 index 0000000..e69de29