Skip to content

Commit

Permalink
Merge pull request '[#311] Logic to automatically generate the docume…
Browse files Browse the repository at this point in the history
…ntation for the dependencies of the integrations' (#330) from 311/auto-generate-deps-docs into master

Reviewed-on: https://git.platypush.tech/platypush/platypush/pulls/330
  • Loading branch information
blacklight committed Sep 30, 2023
2 parents 5025d89 + 343972b commit e6f05df
Show file tree
Hide file tree
Showing 171 changed files with 6,727 additions and 5,419 deletions.
2 changes: 1 addition & 1 deletion .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ steps:

commands:
- echo "Installing required build dependencies"
- apk add --update --no-cache make py3-sphinx py3-pip py3-paho-mqtt
- apk add --update --no-cache make py3-sphinx py3-pip py3-paho-mqtt py3-yaml
- pip install -U hid sphinx-rtd-theme sphinx-book-theme
- pip install .
- mkdir -p /docs/current
Expand Down
190 changes: 190 additions & 0 deletions docs/source/_ext/add_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import inspect
import os
import re
import sys
import textwrap as tw
from contextlib import contextmanager

from sphinx.application import Sphinx

base_path = os.path.abspath(
os.path.join(os.path.dirname(os.path.relpath(__file__)), '..', '..', '..')
)

sys.path.insert(0, base_path)

from platypush.utils import get_plugin_name_by_class # noqa
from platypush.utils.mock import mock # noqa
from platypush.utils.reflection import IntegrationMetadata, import_file # noqa


class IntegrationEnricher:
@staticmethod
def add_events(source: list[str], manifest: IntegrationMetadata, idx: int) -> int:
if not manifest.events:
return idx

source.insert(
idx,
'Triggered events\n----------------\n\n'
+ '\n'.join(
f'\t- :class:`{event.__module__}.{event.__qualname__}`'
for event in manifest.events
)
+ '\n\n',
)

return idx + 1

@staticmethod
def add_actions(source: list[str], manifest: IntegrationMetadata, idx: int) -> int:
if not (manifest.actions and manifest.cls):
return idx

source.insert(
idx,
'Actions\n-------\n\n'
+ '\n'.join(
f'\t- `{get_plugin_name_by_class(manifest.cls)}.{action} '
+ f'<#{manifest.cls.__module__}.{manifest.cls.__qualname__}.{action}>`_'
for action in sorted(manifest.actions.keys())
)
+ '\n\n',
)

return idx + 1

@staticmethod
def _shellify(title: str, cmd: str) -> str:
return f'**{title}**\n\n' + '.. code-block:: bash\n\n\t' + cmd + '\n\n'

@classmethod
def add_install_deps(
cls, source: list[str], manifest: IntegrationMetadata, idx: int
) -> int:
deps = manifest.deps
parsed_deps = {
'before': deps.before,
'pip': deps.pip,
'after': deps.after,
}

if not (any(parsed_deps.values()) or deps.by_pkg_manager):
return idx

source.insert(idx, 'Dependencies\n------------\n\n')
idx += 1

if parsed_deps['before']:
source.insert(idx, cls._shellify('Pre-install', '\n'.join(deps.before)))
idx += 1

if parsed_deps['pip']:
source.insert(idx, cls._shellify('pip', 'pip ' + ' '.join(deps.pip)))
idx += 1

for pkg_manager, sys_deps in deps.by_pkg_manager.items():
if not sys_deps:
continue

source.insert(
idx,
cls._shellify(
pkg_manager.value.default_os.value.description,
pkg_manager.value.install_doc + ' ' + ' '.join(sys_deps),
),
)

idx += 1

if parsed_deps['after']:
source.insert(idx, cls._shellify('Post-install', '\n'.join(deps.after)))
idx += 1

return idx

@classmethod
def add_description(
cls, source: list[str], manifest: IntegrationMetadata, idx: int
) -> int:
docs = (
doc
for doc in (
inspect.getdoc(manifest.cls) or '',
manifest.constructor.doc if manifest.constructor else '',
)
if doc
)

if not docs:
return idx

docstring = '\n\n'.join(docs)
source.insert(idx, f"Description\n-----------\n\n{docstring}\n\n")
return idx + 1

@classmethod
def add_conf_snippet(
cls, source: list[str], manifest: IntegrationMetadata, idx: int
) -> int:
source.insert(
idx,
tw.dedent(
f"""
Configuration
-------------
.. code-block:: yaml
{tw.indent(manifest.config_snippet, ' ')}
"""
),
)

return idx + 1

def __call__(self, _: Sphinx, doc: str, source: list[str]):
if not (source and re.match(r'^platypush/(backend|plugins)/.*', doc)):
return

src = [src.split('\n') for src in source][0]
if len(src) < 3:
return

manifest_file = os.path.join(
base_path,
*doc.split(os.sep)[:-1],
*doc.split(os.sep)[-1].split('.'),
'manifest.yaml',
)

if not os.path.isfile(manifest_file):
return

with mock_imports():
manifest = IntegrationMetadata.from_manifest(manifest_file)
idx = self.add_description(src, manifest, idx=3)
idx = self.add_conf_snippet(src, manifest, idx=idx)
idx = self.add_install_deps(src, manifest, idx=idx)
idx = self.add_events(src, manifest, idx=idx)
idx = self.add_actions(src, manifest, idx=idx)

src.insert(idx, '\n\nModule reference\n----------------\n\n')
source[0] = '\n'.join(src)


@contextmanager
def mock_imports():
conf_mod = import_file(os.path.join(base_path, 'docs', 'source', 'conf.py'))
mock_mods = getattr(conf_mod, 'autodoc_mock_imports', [])
with mock(*mock_mods):
yield


def setup(app: Sphinx):
app.connect('source-read', IntegrationEnricher())
return {
'version': '0.1',
'parallel_read_safe': True,
'parallel_write_safe': True,
}
3 changes: 0 additions & 3 deletions docs/source/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@ Backends
platypush/backend/google.pubsub.rst
platypush/backend/gps.rst
platypush/backend/http.rst
platypush/backend/inotify.rst
platypush/backend/joystick.rst
platypush/backend/joystick.jstest.rst
platypush/backend/joystick.linux.rst
platypush/backend/kafka.rst
platypush/backend/log.http.rst
platypush/backend/mail.rst
platypush/backend/midi.rst
platypush/backend/mqtt.rst
platypush/backend/music.mopidy.rst
platypush/backend/music.mpd.rst
platypush/backend/music.snapcast.rst
Expand All @@ -52,4 +50,3 @@ Backends
platypush/backend/weather.darksky.rst
platypush/backend/weather.openweathermap.rst
platypush/backend/wiimote.rst
platypush/backend/zwave.mqtt.rst
13 changes: 3 additions & 10 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,14 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath("./_ext"))


# -- Project information -----------------------------------------------------

project = 'Platypush'
copyright = '2017-2021, Fabio Manganiello'
author = 'Fabio Manganiello'
copyright = '2017-2023, Fabio Manganiello'
author = 'Fabio Manganiello <fabio@manganiello.tech>'

# The short X.Y version
version = ''
Expand All @@ -52,6 +49,7 @@
'sphinx.ext.githubpages',
'sphinx_rtd_theme',
'sphinx_marshmallow',
'add_dependencies',
]

# Add any paths that contain templates here, relative to this directory.
Expand Down Expand Up @@ -190,11 +188,6 @@
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}

# -- Options for todo extension ----------------------------------------------

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True

autodoc_default_options = {
'members': True,
'show-inheritance': True,
Expand Down
6 changes: 0 additions & 6 deletions docs/source/platypush/backend/inotify.rst

This file was deleted.

6 changes: 0 additions & 6 deletions docs/source/platypush/backend/mqtt.rst

This file was deleted.

5 changes: 0 additions & 5 deletions docs/source/platypush/backend/zwave.mqtt.rst

This file was deleted.

6 changes: 0 additions & 6 deletions docs/source/platypush/plugins/http.request.rss.rst

This file was deleted.

1 change: 0 additions & 1 deletion docs/source/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ Plugins
platypush/plugins/graphite.rst
platypush/plugins/hid.rst
platypush/plugins/http.request.rst
platypush/plugins/http.request.rss.rst
platypush/plugins/http.webpage.rst
platypush/plugins/ifttt.rst
platypush/plugins/inputs.rst
Expand Down
26 changes: 13 additions & 13 deletions platypush/backend/adafruit/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,17 @@

from platypush.backend import Backend
from platypush.context import get_plugin
from platypush.message.event.adafruit import ConnectedEvent, DisconnectedEvent, \
FeedUpdateEvent
from platypush.message.event.adafruit import (
ConnectedEvent,
DisconnectedEvent,
FeedUpdateEvent,
)


class AdafruitIoBackend(Backend):
"""
Backend that listens to messages received over the Adafruit IO message queue
Triggers:
* :class:`platypush.message.event.adafruit.ConnectedEvent` when the
backend connects to the Adafruit queue
* :class:`platypush.message.event.adafruit.DisconnectedEvent` when the
backend disconnects from the Adafruit queue
* :class:`platypush.message.event.adafruit.FeedUpdateEvent` when an
update event is received on a monitored feed
Requires:
* The :class:`platypush.plugins.adafruit.io.AdafruitIoPlugin` plugin to
Expand All @@ -33,6 +27,7 @@ def __init__(self, feeds, *args, **kwargs):

super().__init__(*args, **kwargs)
from Adafruit_IO import MQTTClient

self.feeds = feeds
self._client: Optional[MQTTClient] = None

Expand All @@ -41,6 +36,7 @@ def _init_client(self):
return

from Adafruit_IO import MQTTClient

plugin = get_plugin('adafruit.io')
if not plugin:
raise RuntimeError('Adafruit IO plugin not configured')
Expand Down Expand Up @@ -80,8 +76,11 @@ def _handler(client, feed, data):
def run(self):
super().run()

self.logger.info(('Initialized Adafruit IO backend, listening on ' +
'feeds {}').format(self.feeds))
self.logger.info(
('Initialized Adafruit IO backend, listening on ' + 'feeds {}').format(
self.feeds
)
)

while not self.should_stop():
try:
Expand All @@ -94,4 +93,5 @@ def run(self):
self.logger.exception(e)
self._client = None


# vim:sw=4:ts=4:et:

0 comments on commit e6f05df

Please sign in to comment.