Python Documentation Guide¶
How documentation works in GehaSoftwareHub: writing docstrings, maintaining module READMEs, and publishing to the documentation site.
How the Documentation System Works¶
The documentation site is built with MkDocs Material and hosted on Cloudflare Pages at:
https://main.gehasoftwarehub.pages.dev
Source of Truth¶
Documentation lives in two places:
docs/dev_guidelines/-- Hand-written developer guides (this file, style guide, etc.)src/**/README.md-- Module-level documentation embedded next to the code
The deploy_docs.py script scans src/ for README.md files, generates wrapper
pages in docs/api/, and builds the site. You never edit files in docs/api/
directly -- they are auto-generated and gitignored.
Project Structure¶
docs/
index.md Hand-written landing page
requirements.txt MkDocs pip dependencies
dev_guidelines/ Hand-written developer guides
api/ Auto-generated (gitignored)
docs_site_utils/
mkdocs.yml MkDocs configuration
deploy_docs.py Build and deploy script
site/ Build output (gitignored)
src/
shared_services/
README.md Module overview (auto-discovered)
file_operations/
README.md Sub-module docs (auto-discovered)
api.py Public API (auto-documented if present)
Publishing Documentation¶
Build and Deploy¶
This single command:
- Scans
src/for all README.md files - Generates wrapper pages in
docs/api/ - Updates the API Reference nav in
mkdocs.yml - Builds the static site with MkDocs
- Deploys to Cloudflare Pages
Build Only (Local Preview)¶
Then open http://127.0.0.1:8000.
One-Time Setup¶
# Install MkDocs dependencies
pip install -r docs/requirements.txt
# Install Cloudflare CLI (for deploy only)
npm install -g wrangler
wrangler login
Writing Module READMEs¶
Every module under src/ that should appear in the documentation needs a
README.md file. The deploy script discovers these automatically.
What Gets Discovered¶
The script scans these top-level directories:
src/shared_services/-- Shared Services categorysrc/custom_widgets/-- Custom Widgets categorysrc/main_hub/-- Main Hub category
Any README.md found recursively under these directories becomes a page.
README Template¶
# Module Name
One-line description of what this module does.
## Usage
\```python
from src.shared_services.my_module.api import my_function
result = my_function("input")
\```
## Key Classes
| Class | Purpose |
|-------|---------|
| `MyClass` | Does something specific |
| `MyOtherClass` | Does something else |
## Notes
- Important constraints or gotchas
- Thread safety information
- Dependencies on other modules
Rules¶
- The first
# Headingbecomes the navigation label on the docs site - Keep READMEs focused on usage and architecture, not implementation details
- If the module has an
api.py, the script adds an auto-generated API Reference section using mkdocstrings (reads docstrings from the code) - No README = no docs page. Add one when you want the module documented.
Adding a New Module to the Docs¶
- Create
src/your_module/README.md - Run
python docs_site_utils/deploy_docs.py - Done. The script discovers it, generates the page, and updates the nav.
Writing Docstrings¶
We use Google Style docstrings. These are read by mkdocstrings to generate
API reference pages for modules that have an api.py.
Module Docstring¶
"""
Observable application runtime state.
Provides a singleton for managing mutable application state with
Qt signals for reactive updates.
Usage:
from src.shared_services.state.app_state import AppState
state = AppState.instance()
state.software_changed.connect(self.on_software_changed)
state.set_active_software("PlantDesign")
"""
Class Docstring¶
class AppState(QObject):
"""
Singleton managing observable application runtime state.
Signals:
software_changed: Emitted when active software changes.
Args: software_name (str), is_active (bool)
network_status_changed: Emitted when network connectivity changes.
Args: connected (bool)
Example:
Basic usage::
state = AppState.instance()
state.software_changed.connect(self.handle_change)
state.set_active_software("PlantDesign")
"""
Method Docstring¶
def set_active_software(self, name: str) -> None:
"""
Set the active software module.
Args:
name: Name of the software module (e.g., "PlantDesign").
Empty string clears the active software.
Emits:
software_changed: With (name, is_active) arguments.
"""
Property Docstring¶
@property
def is_software_active(self) -> bool:
"""Check if any software module is currently active."""
return self._software_active
When to Write Docstrings¶
Always document:
- Public classes and their
__init__ - Public methods with non-obvious behavior
- Signals and what triggers them
- Thread safety constraints
- Methods in
api.pyfiles (these appear on the docs site)
Skip documentation for:
- Trivial getters (
return self._name) - Private helper methods (
_internal_method) __str__,__repr__and similar obvious overrides
Docstring Sections Reference¶
"""
Brief description.
Longer description if needed.
Args:
param: Description of parameter.
Returns:
Description of return value.
Raises:
ValueError: When something is invalid.
Example:
Usage example::
result = my_function("input")
Note:
Additional context or caveats.
Warning:
Important safety information.
See Also:
:class:`RelatedClass`: Description.
"""
Type Hints¶
Use type hints on all public APIs. Mkdocstrings reads them automatically, so you do not need to repeat types in the docstring.
# Type hints in signature, descriptions in docstring
def load(self, file_path: Path, encoding: str = "utf-8") -> bool:
"""
Load data from file.
Args:
file_path: Path to the file to load.
encoding: File encoding. Defaults to UTF-8.
Returns:
True if loading succeeded.
"""
PySide6 Type Hints¶
from typing import Optional
from PySide6.QtCore import QObject, Signal
from PySide6.QtWidgets import QWidget
class MyWidget(QWidget):
"""Custom widget with typed signals."""
value_changed: Signal = Signal(int)
def __init__(self, parent: Optional[QWidget] = None):
super().__init__(parent)
self._value: int = 0
Modern Syntax (Python 3.10+)¶
# Use | instead of Union/Optional
def process(value: int | str) -> bool | None:
"""Process a value."""
pass
Writing Examples in Docstrings¶
Use code blocks with :: syntax. Do not use >>> prefixes (they trigger
IDE warnings and are meant for doctest).
def complex_operation(a: int, b: int) -> int:
"""
Perform complex operation.
Example:
Basic usage::
result = complex_operation(5, 3)
# result: 8
With negative numbers::
result = complex_operation(-5, 3)
# result: -2
"""
PySide6-Specific Documentation¶
Documenting Signals¶
List signals in the class docstring with their arguments and when they fire:
class Worker(QRunnable):
"""
Background worker for long-running tasks.
Signals (via nested Signals class):
progress(int): Percentage complete (0-100).
finished(dict): Result dictionary with 'status' and 'data' keys.
error(str): Error message if the task failed.
"""
Documenting Thread Safety¶
def upload_data(self, data: bytes) -> None:
"""
Upload data to the server.
This method starts a background thread via QThreadPool.
Results are delivered via the upload_finished signal
on the main thread.
Note:
Thread-safe. Can be called from any thread.
"""
PyCharm Integration¶
Docstring Format¶
Settings -> Tools -> Python Integrated Tools -> Docstring format: select Google
Useful Shortcuts¶
Ctrl+Q: Quick Documentation popupCtrl+P: Parameter Info- Type
"""+ Enter inside a function: auto-generates docstring skeleton
Editing the Documentation Site¶
Changing Developer Guidelines¶
Edit files directly in docs/dev_guidelines/. Changes appear on the site
after the next deploy.
Changing the Landing Page¶
Edit docs/index.md.
Changing the MkDocs Configuration¶
Edit docs_site_utils/mkdocs.yml. The nav section for "API Reference" is
auto-generated by the deploy script -- manual edits there will be overwritten.
The "Home" and "Developer Guide" nav sections are safe to edit manually.
Adding MkDocs Features to Markdown¶
The site supports these Markdown extensions:
Admonitions (callout boxes):
!!! note "Title"
Content of the note.
!!! warning "Title"
Content of the warning.
??? info "Collapsible Section"
This is collapsed by default.
Code blocks with syntax highlighting:
```python title="example.py"
from src.shared_services.logging.logger_factory import get_logger
logger = get_logger()
```
Content tabs:
Quick Reference¶
Day-to-Day Workflow¶
- Write code with Google Style docstrings
- Add or update
README.mdin your module - Run
python docs_site_utils/deploy_docs.py - Documentation is live
Checklist for New Modules¶
- [ ]
README.mdexists with# Headingand usage examples - [ ] Public API has docstrings (especially
api.py) - [ ] Type hints on all public methods
- [ ] Run deploy script to verify the page renders correctly