98 lines
3.1 KiB
Python
98 lines
3.1 KiB
Python
import importlib
|
|
import inspect
|
|
import json
|
|
import os
|
|
from random import randint
|
|
import re
|
|
import textwrap
|
|
from typing_extensions import override
|
|
|
|
from marshmallow import fields
|
|
|
|
import platypush.schemas
|
|
|
|
from ._base import Parser
|
|
|
|
|
|
class SchemaParser(Parser):
|
|
"""
|
|
Support for response/message schemas in the docs. Format: ``.. schema:: rel_path.SchemaClass(arg1=value1, ...)``,
|
|
where ``rel_path`` is the path of the schema relative to ``platypush/schemas``.
|
|
"""
|
|
|
|
_schemas_path = os.path.dirname(inspect.getfile(platypush.schemas))
|
|
_schema_regex = re.compile(
|
|
r'^(\s*)\.\.\s+schema::\s*([a-zA-Z0-9._]+)\s*(\((.+?)\))?', re.MULTILINE
|
|
)
|
|
|
|
@classmethod
|
|
def _get_field_value(cls, field):
|
|
metadata = getattr(field, 'metadata', {})
|
|
if metadata.get('example'):
|
|
return metadata['example']
|
|
if metadata.get('description'):
|
|
return metadata['description']
|
|
|
|
if isinstance(field, fields.Number):
|
|
return randint(1, 99)
|
|
if isinstance(field, fields.Boolean):
|
|
return bool(randint(0, 1))
|
|
if isinstance(field, fields.URL):
|
|
return 'https://example.org'
|
|
if isinstance(field, fields.List):
|
|
return [cls._get_field_value(field.inner)]
|
|
if isinstance(field, fields.Dict):
|
|
return {
|
|
cls._get_field_value(field.key_field)
|
|
if field.key_field
|
|
else 'key': cls._get_field_value(field.value_field)
|
|
if field.value_field
|
|
else 'value'
|
|
}
|
|
if isinstance(field, fields.Nested):
|
|
ret = {
|
|
name: cls._get_field_value(f)
|
|
for name, f in field.nested().fields.items()
|
|
}
|
|
|
|
return [ret] if field.many else ret
|
|
|
|
return str(field.__class__.__name__).lower()
|
|
|
|
@override
|
|
@classmethod
|
|
def parse(cls, docstring: str) -> str:
|
|
while True:
|
|
m = cls._schema_regex.search(docstring)
|
|
if not m:
|
|
break
|
|
|
|
schema_module_name = '.'.join(
|
|
['platypush.schemas', *(m.group(2).split('.')[:-1])]
|
|
)
|
|
schema_module = importlib.import_module(schema_module_name)
|
|
schema_class = getattr(schema_module, m.group(2).split('.')[-1])
|
|
schema_args = eval(f'dict({m.group(4)})') if m.group(4) else {}
|
|
schema = schema_class(**schema_args)
|
|
parsed_schema = {
|
|
name: cls._get_field_value(field)
|
|
for name, field in schema.fields.items()
|
|
if not field.load_only
|
|
}
|
|
|
|
if schema.many:
|
|
parsed_schema = [parsed_schema]
|
|
|
|
padding = m.group(1)
|
|
docstring = cls._schema_regex.sub(
|
|
textwrap.indent('\n\n.. code-block:: json\n\n', padding)
|
|
+ textwrap.indent(
|
|
json.dumps(parsed_schema, sort_keys=True, indent=2),
|
|
padding + ' ',
|
|
).replace('\n\n', '\n')
|
|
+ '\n\n',
|
|
docstring,
|
|
)
|
|
|
|
return docstring
|