Skip to content

Commit 74e526a

Browse files
[DOCUMENTATION] Implemented #240
Additions with file docs/utils.py: - moved validation and formating functions into utilities module. Changes in file docs/conf.py: - moved `_validate_git_ref` function - added `sphinx_design` extension - improved configuration settings for markdown docs Changes in file docs/requirements.txt: - added `sphinx_design` for documentation building Changes in file pyproject.toml: - aligned tool settings with CEP-7
1 parent d8bd1de commit 74e526a

File tree

4 files changed

+168
-56
lines changed

4 files changed

+168
-56
lines changed

docs/conf.py

Lines changed: 37 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,13 @@
4040
import sys
4141
import os
4242
from urllib.parse import quote
43-
import re
44-
45-
46-
def _validate_git_ref(ref: str) -> str:
47-
"""
48-
Validate if the provided string is a valid Git reference.
49-
50-
Args:
51-
ref (str) -- The Git reference to validate.
52-
53-
Returns:
54-
str -- The validated Git reference.
55-
56-
Raises:
57-
ValueError -- If the reference contains invalid characters.
58-
59-
Meta-Testing:
60-
61-
Testcase 1: Valid reference.
62-
63-
>>> _validate_git_ref('main')
64-
'main'
65-
66-
Testcase 2: Valid reference with special characters.
67-
68-
>>> _validate_git_ref('feature/new-feature')
69-
'feature/new-feature'
70-
71-
Testcase 3: Invalid reference with disallowed characters.
72-
73-
>>> _validate_git_ref('invalid$ref') #doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
74-
Traceback (most recent call last):
75-
...
76-
ValueError: Invalid Git reference: invalid$ref
77-
78-
Testcase 4: Empty reference.
79-
80-
>>> _validate_git_ref('') #doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
81-
Traceback (most recent call last):
82-
...
83-
ValueError: Invalid Git reference:...
84-
"""
85-
if not re.match(r'^[a-zA-Z0-9_\-./]+$', ref):
86-
raise ValueError(f"Invalid Git reference: {ref}")
87-
return ref
8843

44+
# If extensions (or modules to document with autodoc) are in another directory,
45+
# add these directories to sys.path here. If the directory is relative to the
46+
# documentation root, use os.path.abspath to make it absolute, like shown here.
47+
sys.path.insert(0, os.path.abspath(""".."""))
48+
from docs.utils import _validate_git_ref
49+
from docs.utils import slugify_header
8950

9051
# Define the branch reference for linkcode_resolve
9152
DOCS_BUILD_REF: str = _validate_git_ref(os.environ.get("DOCS_BUILD_REF", "stable"))
@@ -98,10 +59,6 @@ def _validate_git_ref(ref: str) -> str:
9859
variable is not set.
9960
"""
10061

101-
# If extensions (or modules to document with autodoc) are in another directory,
102-
# add these directories to sys.path here. If the directory is relative to the
103-
# documentation root, use os.path.abspath to make it absolute, like shown here.
104-
sys.path.insert(0, os.path.abspath(""".."""))
10562
sys.path.insert(1, os.path.abspath("""multicast"""))
10663
sys.path.insert(1, os.path.abspath("""tests"""))
10764

@@ -116,7 +73,7 @@ def _validate_git_ref(ref: str) -> str:
11673
# for rst use 'sphinx.ext.autodoc'
11774
extensions = [
11875
"""sphinx.ext.napoleon""", """autodoc2""", """sphinx.ext.autosectionlabel""",
119-
"""sphinx.ext.githubpages""", """myst_parser""",
76+
"""sphinx.ext.githubpages""", """myst_parser""", """sphinx_design""",
12077
"""sphinx.ext.autosummary""", """sphinx.ext.doctest""", """sphinx.ext.todo""",
12178
"""sphinx.ext.linkcode""", """sphinx.ext.viewcode""", """sphinx.ext.intersphinx""",
12279
]
@@ -321,19 +278,46 @@ def _validate_git_ref(ref: str) -> str:
321278
# see https://myst-parser.readthedocs.io/en/latest/syntax/roles-and-directives.html
322279

323280
# be more like GFM with style
324-
myst_enable_extensions = ("tasklist", "strikethrough", "fieldlist")
281+
myst_enable_extensions = ("tasklist", "strikethrough", "fieldlist", "linkify")
325282

326283
# for GFM diagrams and interoperability with other Markdown renderers
327284
myst_fence_as_directive = ("mermaid", "suggestion", "note")
328285

286+
# Add linkify configuration
287+
myst_linkify_fuzzy_links = False
288+
329289
# Focus only on github markdown
330-
# myst_gfm_only = True
290+
myst_gfm_only = True
291+
292+
myst_html_meta = {
293+
"github_url": f"https://github.com/reactive-firewall/{project}"
294+
}
295+
296+
# For GH-style admonitions to MyST conversion
297+
myst_admonition_aliases = {
298+
"note": "note",
299+
"warning": "warning",
300+
"important": "important",
301+
"tip": "tip",
302+
"caution": "caution"
303+
}
331304

332305
# how deep should markdown headers have anchors be generated
333306
heading_anchors = 3
334307

308+
# Enable header anchors as requested
309+
myst_heading_anchors = 3
310+
311+
# For better slug generation in header references
312+
myst_heading_slug_func = slugify_header
313+
335314
# -- Options for napoleon ext --------------------------------------------------
336315

316+
napoleon_google_docstring = True
317+
napoleon_numpy_docstring = False
318+
napoleon_include_private_with_doc = False
319+
napoleon_include_special_with_doc = False
320+
337321
# include __init__ when it has docstrings
338322
napoleon_include_init_with_doc = True
339323

@@ -432,9 +416,7 @@ def _validate_git_ref(ref: str) -> str:
432416

433417
# -- Link resolver -------------------------------------------------------------
434418

435-
linkcode_url_prefix: str = str(
436-
"""https://github.com/reactive-firewall/{proj}"""
437-
).format(proj=project)
419+
linkcode_url_prefix: str = f"https://github.com/reactive-firewall/{project}"
438420

439421
extlinks = {
440422
"""issue""": (

docs/requirements.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ pip>=24.3.1
3636
# build - MIT license
3737
build>=1.2.1, !=1.2.2.post1
3838
# sphinx - BSD license
39-
sphinx>=7.0
39+
sphinx>=7.3
4040
# sphinx-autodoc2 - MIT license
4141
sphinx-autodoc2>=0.5.0
4242
# myst-parser - MIT license
4343
myst-parser[linkify]>=4.0.0
4444
# sphinxawesome-theme - MIT license
4545
sphinxawesome-theme>=5.2
46+
# sphinx_design - BSD license
47+
sphinx_design>=0.6.1

docs/utils.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# Multicast Documentation Utilities
4+
# ..................................
5+
# Copyright (c) 2024-2025, Mr. Walls
6+
# ..................................
7+
# Licensed under MIT (the "License");
8+
# you may not use this file except in compliance with the License.
9+
# You may obtain a copy of the License at
10+
# ..........................................
11+
# https://www.github.com/reactive-firewall/multicast/LICENSE.md
12+
# ..........................................
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
19+
import re
20+
21+
def _validate_git_ref(ref: str) -> str:
22+
"""
23+
Validate if the provided string is a valid Git reference.
24+
25+
Args:
26+
ref (str) -- The Git reference to validate.
27+
28+
Returns:
29+
str -- The validated Git reference.
30+
31+
Raises:
32+
ValueError -- If the reference contains invalid characters.
33+
34+
Meta-Testing:
35+
36+
Testcase 1: Valid reference.
37+
38+
>>> _validate_git_ref('main')
39+
'main'
40+
41+
Testcase 2: Valid reference with special characters.
42+
43+
>>> _validate_git_ref('feature/new-feature')
44+
'feature/new-feature'
45+
46+
Testcase 3: Invalid reference with disallowed characters.
47+
48+
>>> _validate_git_ref('invalid$ref') #doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
49+
Traceback (most recent call last):
50+
...
51+
ValueError: Invalid Git reference: invalid$ref
52+
53+
Testcase 4: Empty reference.
54+
55+
>>> _validate_git_ref('') #doctest: +IGNORE_EXCEPTION_DETAIL +ELLIPSIS
56+
Traceback (most recent call last):
57+
...
58+
ValueError: Invalid Git reference:...
59+
"""
60+
if not re.match(r'^[a-zA-Z0-9_\-./]+$', ref):
61+
raise ValueError(f"Invalid Git reference: {ref}")
62+
return ref
63+
64+
65+
def slugify_header(s: str) -> str:
66+
"""
67+
Convert header text to a URL-friendly slug.
68+
69+
This function transforms header text into a URL-friendly slug by removing special characters,
70+
converting to lowercase, and replacing consecutive spaces or dashes with a single dash.
71+
The resulting slug is suitable for use in header anchors and URL paths.
72+
73+
Arguments:
74+
s (str) -- The header text to be converted into a slug.
75+
76+
Returns:
77+
str -- A URL-friendly slug derived from the input text.
78+
79+
Unit-Testing:
80+
81+
Testcase 1: Basic header with spaces and special characters.
82+
83+
>>> slugify_header("Hello, World!")
84+
'hello-world'
85+
86+
Testcase 2: Header with multiple spaces and mixed case.
87+
88+
>>> slugify_header(" API Documentation ")
89+
'api-documentation'
90+
91+
Testcase 3: Header with consecutive spaces and dashes.
92+
93+
>>> slugify_header("API -- Documentation")
94+
'api-documentation'
95+
96+
Testcase 4: Header with Unicode characters and accents.
97+
98+
>>> slugify_header("über café 123")
99+
'über-café-123'
100+
101+
Testcase 5: Header with special markdown characters.
102+
103+
>>> slugify_header("[CEP-7] Documentation *Guide*")
104+
'cep-7-documentation-guide'
105+
"""
106+
# First, remove special characters and convert to lowercase
107+
text = re.sub(r'[^\w\- ]', '', s).strip().lower()
108+
# Then replace consecutive spaces or dashes with a single dash
109+
return re.sub(r'[-\s]+', '-', text)

pyproject.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22
requires = ["setuptools>=75.0", "build>=1.2.1", "wheel>=0.44"]
33
build-backend = "setuptools.build_meta"
44

5+
[tool.flake8]
6+
# OPTIONAL - BCP selection
7+
# select = ["C", "E", "F", "W", "B", "B950"]
8+
# CEP-7 specific
9+
extend-select = ["D", "E"]
10+
# OPTIONAL - BCP Ignore specific warnings and errors according to CEP-8 style
11+
# ignore = ["W191", "W391", "E117"]
12+
# Ignore specific warnings and errors according to CEP-7 style
13+
extend-ignore = ["E117", "D203", "D208", "D212"]
14+
# REQUIRED CEP-7 Custom Exceptions:
15+
# E117, # Over-indented - RECCOMENDED
16+
# D208, # Docstring is over-indented - CEP-7
17+
# D203, # 1 blank line required before class docstring - CEP-7
18+
# D212, # Multi-line docstring summary should start at the first line - CEP-7
19+
# OPTIONAL - BCP Ignore long lines as specified in CEP-8
20+
max-line-length = 100
21+
docstring-convention = "google"
22+
docstring_style = "google"
23+
524
[pytest.enabler.flake8]
625
addopts = "--flake8"
726

0 commit comments

Comments
 (0)