Compare commits

...

4 Commits

Author SHA1 Message Date
Nicolas c0c774f8e3 Merge branch 'main' of https://github.com/Nonannet/pyhoff 2025-06-06 16:37:03 +02:00
Nicolas 3de52ae748 Code style fixed 2025-06-06 16:36:55 +02:00
Nicolas 3771cfad05 Docstrings updated 2025-06-06 16:34:51 +02:00
Nicolas 781816334d docs restructured and updated 2025-06-06 16:34:39 +02:00
9 changed files with 68 additions and 57 deletions

View File

@ -17,15 +17,13 @@ jobs:
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:
python-version: "3.x" python-version: "3.x"
- name: Install dependencies - name: Install package and dependencies
run: pip install sphinx sphinx_rtd_theme sphinx-autodoc-typehints myst-parser run: pip install .[doc_build]
- name: Generate Class List - name: Generate Class List
run: | run: python ./docs/source/generate_class_list.py
pip install .
python ./docs/source/generate_class_list.py
- name: Build Docs - name: Build Docs
run: | run: |
cp LICENSE docs/source/ cp LICENSE docs/source/LICENSE.md
cd docs cd docs
sphinx-apidoc -o ./source/ ../src/ -M --no-toc sphinx-apidoc -o ./source/ ../src/ -M --no-toc
rm ./source/*.rst rm ./source/*.rst

1
.gitignore vendored
View File

@ -70,6 +70,7 @@ instance/
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
docs/source/_autogenerated/
# Autogenerated documentation # Autogenerated documentation
docs/source/modules.md docs/source/modules.md

View File

@ -26,7 +26,8 @@ exclude_patterns = []
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
# html_theme = 'alabaster' # html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme' html_theme = 'pydata_sphinx_theme'
html_static_path = ['_static'] html_static_path = ['_static']
autodoc_inherit_docstrings = True autodoc_inherit_docstrings = True
autoclass_content = 'both'

View File

@ -2,48 +2,64 @@ import importlib
import inspect import inspect
import fnmatch import fnmatch
from io import TextIOWrapper from io import TextIOWrapper
import os
def write_classes(f: TextIOWrapper, patterns: list[str], module_name: str, title: str, description: str = '', exclude: list[str] = []) -> None: def write_classes(f: TextIOWrapper, patterns: list[str], module_name: str, title: str, description: str = '', exclude: list[str] = []) -> None:
"""Write the classes to the file."""
module = importlib.import_module(module_name) module = importlib.import_module(module_name)
classes = [ classes = [
name for name, obj in inspect.getmembers(module, inspect.isclass) name for name, obj in inspect.getmembers(module, inspect.isclass)
if (obj.__module__ == module_name and if (obj.__module__ == module_name and
any(fnmatch.fnmatch(name, pat) for pat in patterns) and any(fnmatch.fnmatch(name, pat) for pat in patterns if pat not in exclude) and
obj.__doc__ and (obj.__doc__ and ('(Automatic generated stub)' not in obj.__doc__ or ' digital ' in obj.__doc__)) and
# '(Automatic generated stub)' not in obj.__doc__ and obj.__doc__ and '(no I/O function)' not in obj.__doc__
'(no I/O function)' not in obj.__doc__) )
] ]
"""Write the classes to the file."""
f.write(f'## {title}\n\n')
if description: if description:
f.write(f'{description}\n\n') f.write(f'{description}\n\n')
write_dochtree(f, title, classes)
for cls in classes: for cls in classes:
if cls not in exclude: with open(f'docs/source/_autogenerated/{cls}.md', 'w') as f2:
f.write('```{eval-rst}\n') f2.write(f'# {module_name}.{cls}\n')
f.write(f'.. autoclass:: {module_name}.{cls}\n') f2.write('```{eval-rst}\n')
f.write(' :members:\n') f2.write(f'.. autoclass:: {module_name}.{cls}\n')
f.write(' :show-inheritance:\n') f2.write(' :members:\n')
f.write(' :inherited-members: object\n') f2.write(' :show-inheritance:\n')
f2.write(' :inherited-members:\n')
if title not in ['Base classes', 'Bus coupler']: if title not in ['Base classes', 'Bus coupler']:
f.write(' :exclude-members: select, parameters\n') f2.write(' :exclude-members: select, parameters\n')
else: f2.write('```\n\n')
print('* ', cls)
f.write('```\n\n')
with open('docs/source/modules.md', 'w') as f: def write_dochtree(f: TextIOWrapper, title: str, items: list[str]):
f.write('# Classes and Modules\n\n') f.write('```{toctree}\n')
write_classes(f, ['BK*', 'WAGO_750_352'], 'pyhoff.devices', title='Bus coupler', f.write(':maxdepth: 1\n')
description='These classes are bus couplers and are used to connect the IO bus terminals to a Ethernet interface.') f.write(f':caption: {title}:\n')
write_classes(f, ['KL*'], 'pyhoff.devices', title='Beckhoff bus terminals') # f.write(':hidden:\n')
write_classes(f, ['WAGO*'], 'pyhoff.devices', title='WAGO bus terminals', exclude=['WAGO_750_352']) for text in items:
write_classes(f, ['*Terminal*'], 'pyhoff.devices', title='Generic bus terminals') if not text.startswith('_'):
write_classes(f, ['*'], 'pyhoff', title='Base classes', f.write(f"{text}\n")
description='These classes are base classes for devices and are typically not used directly.') f.write('```\n\n')
write_classes(f, ['*'], 'pyhoff.modbus', title='Modbus',
description='This modbus implementation is used internally.')
if __name__ == "__main__":
# Ensure the output directory exists
os.makedirs('docs/source/_autogenerated', exist_ok=True)
with open('docs/source/_autogenerated/index.md', 'w') as f:
f.write('# Classes and Modules\n\n')
write_classes(f, ['BK*', 'WAGO_750_352'], 'pyhoff.devices', title='Bus coupler',
description='These classes are bus couplers and are used to connect the IO bus terminals to a Ethernet interface.')
write_classes(f, ['KL*'], 'pyhoff.devices', title='Beckhoff bus terminals')
write_classes(f, ['WAGO*'], 'pyhoff.devices', title='WAGO bus terminals', exclude=['WAGO_750_352'])
write_classes(f, ['*Terminal*'], 'pyhoff.devices', title='Generic bus terminals')
write_classes(f, ['*'], 'pyhoff', title='Base classes',
description='These classes are base classes for devices and are typically not used directly.')
write_classes(f, ['*'], 'pyhoff.modbus', title='Modbus',
description='This modbus implementation is used internally.')

View File

@ -1,9 +1,7 @@
```{toctree} ```{toctree}
:maxdepth: 2 :maxdepth: 1
:caption: Contents: :hidden:
_autogenerated/index
readme
modules
``` ```
```{include} ../../README.md ```{include} ../../README.md

View File

@ -1,2 +0,0 @@
```{include} ../../README.md
```

View File

@ -29,6 +29,12 @@ where = ["src"]
dev = [ dev = [
"pytest", "flake8", "mypy" "pytest", "flake8", "mypy"
] ]
doc_build = [
"sphinx",
"pydata_sphinx_theme",
"sphinx-autodoc-typehints",
"myst-parser"
]
[tool.mypy] [tool.mypy]
files = ["src"] files = ["src"]

View File

@ -226,9 +226,9 @@ class BusCoupler():
Base class for ModBus TCP bus coupler Base class for ModBus TCP bus coupler
Attributes: Attributes:
bus_terminals: A list of bus terminal classes according to the bus_terminals (list[BusTerminal]): A list of bus terminal classes according to the
connected terminals. connected terminals.
modbus: The underlying modbus client used for the connection. modbus (SimpleModbusClient): The underlying modbus client used for the connection.
""" """
def __init__(self, host: str, port: int = 502, bus_terminals: Iterable[type[BusTerminal]] = [], def __init__(self, host: str, port: int = 502, bus_terminals: Iterable[type[BusTerminal]] = [],

View File

@ -48,20 +48,13 @@ class SimpleModbusClient:
""" """
A simple Modbus TCP client A simple Modbus TCP client
Args:
host: hostname or IP address
port: server port
unit_id: ModBus id
timeout: socket timeout in seconds
debug: if True prints out transmitted and received bytes in hex
Attributes: Attributes:
host: hostname or IP address host (str): hostname or IP address
port: server port port (int): server port
unit_id: ModBus id unit_id (int): ModBus id
timeout: socket timeout in seconds timeout (float): socket timeout in seconds
last_error: contains last error message or empty string if no error occurred last_error (str): contains last error message or empty string if no error occurred
debug: if True prints out transmitted and received bytes in hex debug (bool): if True prints out transmitted and received bytes in hex
""" """