Skip to content

Commit

Permalink
Create markdown docs from docstring in py files (#2181)
Browse files Browse the repository at this point in the history
* adding pydoc-markdown yml

* add lazydocs

* run lazydocs and pydoc-markdown parallel for testing

* fix output path

* add created docs to source control

* fix the path to source code

* change filter

* revert last commit

* use only pydoc-markdown

* remove the test files from version control

* rename script and add to pre-commit hook

* change py file to test pre commit hook

* modify py file again

* test markdown formatting

* updated docstring

* use sphinx renderer

* update markdown

* convert links and formatting to markdown

* make link to plugin docs

* fix comment

* fix wrong docstring

* add more fixes to doc

* update docstring-md

* rename to README.md so github picks it up directly

* fix formatting

* improve docs

* improve docs

* fix docs

* updte docstream md

* fix formatting for md

* fix formatting to md and check crossref

* fix links

* fix formatting for notes

* fix links

* revert wrong fix

* fixed indentation

* add generated docstring

* Check for docstring in action

* Try without request changes

* try request changes

* remove docstring check from action
  • Loading branch information
s-martin authored Jan 16, 2024
1 parent 03e6197 commit 4f015dd
Show file tree
Hide file tree
Showing 35 changed files with 6,241 additions and 287 deletions.
14 changes: 13 additions & 1 deletion .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# Checks
# - flake8 on staged python files
# Note: This only checks the modified files
# - docs build of if any python file or any doc file is staged
# - docs build of if any python file is staged
# Note: This builds the entire documentation if a changed file goes into the documentation
#
# If there are problem with this script, commit may still be done with
Expand All @@ -28,6 +28,18 @@ fi

code=$(( flake8_code ))

doc_code=0
if [[ -n $PY_FILES ]]; then
echo -e "\n**************************************************************"
echo -e "Modified Python source files. Generation markdown docs from docstring ... \n"
echo -e "**************************************************************\n"
./run_docgeneration.sh -c
doc_code=$?
echo "pydoc_markdown return code: $doc_code"
fi

code=$(( flake8_code + doc_code ))

if [[ code -gt 0 ]]; then
echo -e "\n**************************************************************"
echo -e "ERROR(s) during pre-commit checks. Aborting commit!"
Expand Down
2 changes: 2 additions & 0 deletions documentation/developers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
* [Jukebox Apps](./coreapps.md)
* [Web App](./webapp.md)
* [RFID Readers](./rfid)
* [Docstring API Docs (from py files)](./docstring/README.md)
* [Plugin Reference](./docstring/README.md#jukeboxplugs)
* [Feature Status](./status.md)
* [Known Issues](./known-issues.md)

Expand Down
5,914 changes: 5,914 additions & 0 deletions documentation/developers/docstring/README.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions documentation/developers/known-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ RUN cd ${HOME} && mkdir ${ZMQ_TMP_DIR} && cd ${ZMQ_TMP_DIR}; \
make && make install
```

[libzmq details](./libzmq.md)

## Configuration

In `jukebox.yaml` (and all other config files):
Expand Down
13 changes: 13 additions & 0 deletions pydoc-markdown.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
loaders:
- type: python
search_path: [./src/jukebox]
processors:
- type: filter
# skip_empty_modules: true # Uncommenting this skips also run_jukebox etc.
- type: sphinx
- type: crossref
renderer:
type: markdown
render_toc: true
filename: ./documentation/developers/docstring/README.md
render_page_title: true
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ flake8>=4.0.0
pytest
pytest-cov
mock

# API docs generation
pydoc-markdown
15 changes: 15 additions & 0 deletions run_docgeneration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

# Runner script for pydoc-markdown to ensure
# - independent from working directory

# Change working directory to location of script
SOURCE=${BASH_SOURCE[0]}
SCRIPT_DIR="$(dirname "$SOURCE")"
cd "$SCRIPT_DIR" || (echo "Could not change to top-level project directory" && exit 1)

# Run pydoc-markdown
# make sure, directory exists
mkdir -p ./documentation/developers/docstring
# expects pydoc-markdown.yml at working dir
pydoc-markdown
Original file line number Diff line number Diff line change
Expand Up @@ -35,42 +35,39 @@


class battmon_ads1015(BatteryMonitorBase.BattmonBase):
'''Battery Monitor based on a ADS1015
"""Battery Monitor based on a ADS1015
CAUTION - WARNING
========================================================================
Lithium and other batteries are dangerous and must be treated with care.
Rechargeable Lithium Ion batteries are potentially hazardous and can
present a serious FIRE HAZARD if damaged, defective or improperly used.
Do not use this circuit to a lithium ion battery without expertise and
training in handling and use of batteries of this type.
Use appropriate test equipment and safety protocols during development.
There is no warranty, this may not work as expected or at all!
=========================================================================
> [!CAUTION]
> Lithium and other batteries are dangerous and must be treated with care.
> Rechargeable Lithium Ion batteries are potentially hazardous and can
> present a serious **FIRE HAZARD** if damaged, defective or improperly used.
> Do not use this circuit to a lithium ion battery without expertise and
> training in handling and use of batteries of this type.
> Use appropriate test equipment and safety protocols during development.
> There is no warranty, this may not work as expected or at all!
This script is intended to read out the Voltage of a single Cell LiIon Battery using a CY-ADS1015 Board:
3.3V
+
|
.----o----.
___ | | SDA
.--------|___|---o----o---------o AIN0 o------
| 2MΩ | | | | SCL
| .-. | | ADS1015 o------
--- | | --- | |
Battery - 1.5MΩ| | ---100nF '----o----'
2.9V-4.2V| '-' | |
| | | |
=== === === ===
3.3V
+
|
.----o----.
___ | | SDA
.--------|___|---o----o---------o AIN0 o------
| 2MΩ | | | | SCL
| .-. | | ADS1015 o------
--- | | --- | |
Battery - 1.5MΩ| | ---100nF '----o----'
2.9V-4.2V| '-' | |
| | | |
=== === === ===
Attention:
- the circuit is constantly draining the battery! (leak current up to: 2.1µA)
- the time between sample needs to be a minimum 1sec with this high impedance voltage divider
* the circuit is constantly draining the battery! (leak current up to: 2.1µA)
* the time between sample needs to be a minimum 1sec with this high impedance voltage divider
don't use the continuous conversion method!
'''
"""

def __init__(self, cfg):
super().__init__(cfg, logger)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
This effectively does:
* register a callback with components.volume to get notified when a new sound card connects
* if that is a bluetooth device, try opening an input device with similar name using
* button listeners are run each in its own thread
* register a callback with components.volume to get notified when a new sound card connects
* if that is a bluetooth device, try opening an input device with similar name using
* button listeners are run each in its own thread
"""
import logging
Expand Down
6 changes: 2 additions & 4 deletions src/jukebox/components/controls/common/evdev_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ def _filter_by_device_name(all_devices: List[evdev.InputDevice],
def find_device(device_name: str, exact_name: bool = True, mandatory_keys: Optional[Set[int]] = None) -> str:
"""Find an input device with device_name and mandatory keys.
Raises
#. FileNotFoundError, if no device is found.
#. AttributeError, if device does not have the mandatory keys
:raise FileNotFoundError: if no device is found.
:raise AttributeError: if device does not have the mandatory key
If multiple devices match, the first match is returned
Expand Down
2 changes: 0 additions & 2 deletions src/jukebox/components/gpio/gpioz/core/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ class VolumeToRGB:
Map input :data:`0...100` to color range :data:`green...magenta` and get the color for level 50
.. code-block:: python
conv = VolumeToRGB(100, offset=120, section=180)
(r, g, b) = conv(50)
Expand Down
37 changes: 19 additions & 18 deletions src/jukebox/components/gpio/gpioz/core/input_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
All callback handlers are replaced by GPIOZ callback handlers. These are usually configured
by using the :func:`set_rpc_actions` each input device exhibits.
For examples how to use the devices from the configuration files, see :ref:`userguide/gpioz:Input devices`
For examples how to use the devices from the configuration files, see
[GPIO: Input Devices](../../builders/gpio.md#input-devices).
"""

import functools
Expand Down Expand Up @@ -75,7 +76,7 @@ def set_rpc_actions(self, action_config) -> None:
Set all input device callbacks from :attr:`action_config`
:param action_config: Dictionary with one
:ref:`RPC Command <userguide/rpc_commands:RPC commands>` definition entry for every device callback
[RPC Commands](../../builders/rpc-commands.md) definition entry for every device callback
"""
pass

Expand Down Expand Up @@ -233,11 +234,11 @@ class LongPressButton(NameMixin, ButtonBase):
"""
A Button that runs a single actions only when the button is pressed long enough
:param pull_up: See `Button`_
:param pull_up: See #Button
:param active_state: See `Button`_
:param active_state: See #Button
:param bounce_time: See `Button`_
:param bounce_time: See #Button
:param hold_repeat: If :data:`True` repeat the :attr:`on_press` every :attr:`hold_time` seconds. Else only action
is run only once independent of the length of time the button is pressed for.
Expand Down Expand Up @@ -291,11 +292,11 @@ class ShortLongPressButton(NameMixin, ButtonBase):
event. Furthermore, if there is a long hold, only the long hold action is executed - the short press action is not run
in this case!
:param pull_up: See `Button`_
:param pull_up: See #Button
:param active_state: See `Button`_
:param active_state: See #Button
:param bounce_time: See `Button`_
:param bounce_time: See #Button
:param hold_time: The time in seconds to differentiate if it is a short or long press. If the button is released before
this time, it is a short press. As soon as the button is held for :attr:`hold_time` it is a long press and the
Expand All @@ -304,9 +305,9 @@ class ShortLongPressButton(NameMixin, ButtonBase):
:param hold_repeat: If :data:`True` repeat the long press action every :attr:`hold_time` seconds after first long press
action
:param pin_factory: See `Button`_
:param pin_factory: See #Button
:param name: See `Button`_
:param name: See #Button
"""
def __init__(
self, pin=None, *, pull_up=True, active_state=None, bounce_time=None,
Expand Down Expand Up @@ -370,11 +371,11 @@ class RotaryEncoder(NameMixin):
"""
A rotary encoder to run one of two actions depending on the rotation direction.
:param bounce_time: See `Button`_
:param bounce_time: See #Button
:param pin_factory: See `Button`_
:param pin_factory: See #Button
:param name: See `Button`_
:param name: See #Button
"""
def __init__(self, a, b, *, bounce_time=None, pin_factory=None, name=None):
super().__init__(name=name)
Expand Down Expand Up @@ -442,11 +443,11 @@ class TwinButton(NameMixin):
It is not necessary to configure all actions.
:param pull_up: See `Button`_
:param pull_up: See #Button
:param active_state: See `Button`_
:param active_state: See #Button
:param bounce_time: See `Button`_
:param bounce_time: See #Button
:param hold_time: The time in seconds to differentiate if it is a short or long press. If the button is released before
this time, it is a short press. As soon as the button is held for :attr:`hold_time` it is a long press and the
Expand All @@ -455,9 +456,9 @@ class TwinButton(NameMixin):
:param hold_repeat: If :data:`True` repeat the long press action every :attr:`hold_time` seconds after first long press
action. A long dual press is never repeated independent of this setting
:param pin_factory: See `Button`_
:param pin_factory: See #Button
:param name: See `Button`_
:param name: See #Button
"""

class StateVar(Enum):
Expand Down
3 changes: 2 additions & 1 deletion src/jukebox/components/gpio/gpioz/core/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ def patch_mock_outputs_with_callback():
This targets to represent the state in the TK GUI.
Other output devices cannot be represented in the GUI and are silently ignored.
..note:: Only for developing purposes!"""
> [!NOTE]
> Only for developing purposes!"""
gpiozero.LED._write_orig = gpiozero.LED._write
gpiozero.LED._write = rewrite
gpiozero.LED.on_change_callback = None
Expand Down
3 changes: 2 additions & 1 deletion src/jukebox/components/gpio/gpioz/core/output_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
with parameters for this device and optional parameters from another device. Unused/unsupported parameters
are silently ignored. This is done to reduce the amount of coding required for connectivity functions.
For examples how to use the devices from the configuration files, see :ref:`userguide/gpioz:Output devices`
For examples how to use the devices from the configuration files, see
[GPIO: Output Devices](../../builders/gpio.md#output-devices).
"""

from typing import Optional, List
Expand Down
12 changes: 6 additions & 6 deletions src/jukebox/components/gpio/gpioz/plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ class ServiceIsRunningCallbacks(CallbackHandler):
"""
Callbacks are executed when
* Jukebox app started
* Jukebox shuts down
* Jukebox app started
* Jukebox shuts down
This is intended to e.g. signal an LED to change state.
This is integrated into this module because:
* we need the GPIO to control a LED (it must be available when the status callback comes)
* the plugin callback functions provide all the functionality to control the status of the LED
* which means no need to adapt other modules
* we need the GPIO to control a LED (it must be available when the status callback comes)
* the plugin callback functions provide all the functionality to control the status of the LED
* which means no need to adapt other modules
"""

def register(self, func: Callable[[int], None]):
Expand All @@ -76,7 +76,7 @@ def register(self, func: Callable[[int], None]):
.. py:function:: func(status: int)
:noindex:
:param status: 1 if app started, 0 if app shuts down
:param status: 1 if app started, 0 if app shuts down
"""
super().register(func)

Expand Down
Loading

0 comments on commit 4f015dd

Please sign in to comment.