kopia lustrzana https://github.com/Yakifo/amqtt
updating docs to pull descriptions from structured configs
rodzic
1ec555ca23
commit
1be3a7c848
|
@ -3,7 +3,7 @@ from dataclasses import dataclass, field, fields, replace, asdict
|
|||
from enum import Enum, StrEnum
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import TYPE_CHECKING, Any, LiteralString, Literal
|
||||
|
||||
from amqtt.mqtt.constants import QOS_0
|
||||
|
||||
|
@ -34,7 +34,7 @@ class ListenerType(StrEnum):
|
|||
|
||||
class Dictable:
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
return self.get(key)
|
||||
|
||||
def get(self, name, default=None):
|
||||
if hasattr(self, name):
|
||||
|
@ -43,34 +43,6 @@ class Dictable:
|
|||
return default
|
||||
raise ValueError(f"'{name}' is not defined")
|
||||
|
||||
def copy(self):
|
||||
return replace(self)
|
||||
|
||||
def update(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
raise TypeError(f'must update with another {self.__class__.__name__}')
|
||||
for f in fields(self):
|
||||
if isinstance(getattr(self, f.name), Dictable):
|
||||
setattr(self, f.name, getattr(self, f.name).update(getattr(other, f.name)))
|
||||
if getattr(self, f.name) == f.default:
|
||||
setattr(self, f.name, other[f.name])
|
||||
|
||||
# if not isinstance(other, self.__class__):
|
||||
# raise TypeError(f'must update with another {self.__class__.__name__}')
|
||||
#
|
||||
# valids = { k:v for k,v in asdict(self).items() if v is not None }
|
||||
# return replace(other, **valids)
|
||||
|
||||
def __ior__(self, other):
|
||||
self.update(other)
|
||||
return self
|
||||
# if not isinstance(other, self.__class__):
|
||||
# raise TypeError(f'must update with another {self.__class__.__name__}')
|
||||
# for f in fields(self):
|
||||
# if getattr(self, f.name) == f.default:
|
||||
# setattr(self, f.name, other[f.name])
|
||||
# return self
|
||||
|
||||
def __contains__(self, name):
|
||||
return getattr(self, name, None) is not None
|
||||
|
||||
|
@ -80,10 +52,13 @@ class Dictable:
|
|||
|
||||
|
||||
@dataclass
|
||||
class ListenerConfig(Dictable):
|
||||
class ListenerConfig:
|
||||
type: ListenerType = ListenerType.TCP
|
||||
"""listener type: 'tcp' or 'ws'"""
|
||||
bind: str | None = "0.0.0.0:1883"
|
||||
"""address and port for the listener to bind to"""
|
||||
max_connections: int = 0
|
||||
""""""
|
||||
ssl: bool = False
|
||||
cafile: str | Path | None = None
|
||||
capath: str | Path | None = None
|
||||
|
@ -96,6 +71,15 @@ class ListenerConfig(Dictable):
|
|||
if isinstance(getattr(self, fn), str):
|
||||
setattr(self, fn, Path(getattr(self, fn)))
|
||||
|
||||
def apply(self, other):
|
||||
"""Apply the field from 'other', if 'self' field is default."""
|
||||
if not isinstance(other, ListenerConfig):
|
||||
msg = f'cannot apply {self.__class__.__name__} to {other.__class__.__name__}'
|
||||
raise TypeError(msg)
|
||||
for f in fields(self):
|
||||
if getattr(self, f.name) == f.default:
|
||||
setattr(self, f.name, other[f.name])
|
||||
|
||||
def default_listeners():
|
||||
return {
|
||||
'default': ListenerConfig()
|
||||
|
@ -112,12 +96,20 @@ def default_broker_plugins():
|
|||
|
||||
@dataclass
|
||||
class BrokerConfig(Dictable):
|
||||
listeners: dict[str, ListenerConfig] = field(default_factory=dict)
|
||||
"""Structured configuration for a broker. Can be passed directly to `amqtt.broker.Broker` or created from a dictionary.
|
||||
"""
|
||||
listeners: dict[Literal['default'] | str, ListenerConfig] = field(default_factory=default_listeners)
|
||||
"""Network of listeners used by the services."""
|
||||
sys_interval: int | None = None
|
||||
"""Deprecated field to configure the `BrokerSysPlugin`"""
|
||||
timeout_disconnect_delay: int | None = 0
|
||||
plugins: dict | list | None = None
|
||||
"""Client disconnect timeout without a keep-alive."""
|
||||
auth: dict[str, Any] | None = None
|
||||
"""Deprecated field used to config EntryPoint-loaded plugins."""
|
||||
topic_check: dict[str, Any] | None = None
|
||||
"""Deprecated field used to config EntryPoint-loaded plugins."""
|
||||
plugins: dict | list | None = None
|
||||
"""A list of strings representing the modules and class name of `BasePlugin`, `BaseAuthPlugin` and `BaseTopicPlugins`."""
|
||||
|
||||
def __post__init__(self) -> None:
|
||||
if self.sys_interval is not None:
|
||||
|
@ -130,7 +122,9 @@ class BrokerConfig(Dictable):
|
|||
|
||||
default_listener = self.listeners['default']
|
||||
for listener_name, listener in self.listeners.items():
|
||||
listener.update(default_listener)
|
||||
if listener_name == 'default':
|
||||
continue
|
||||
listener.apply(default_listener)
|
||||
|
||||
if isinstance(self.plugins, list):
|
||||
_plugins = {}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block outdated %}
|
||||
You're not viewing the latest version.
|
||||
<a href="{{ '../' ~ base_url }}">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
{% endblock %}
|
|
@ -3,6 +3,22 @@
|
|||
This configuration structure is valid as a python dictionary passed to the `amqtt.broker.Broker` class's `__init__` method or
|
||||
as a yaml formatted file passed to the `amqtt` script.
|
||||
|
||||
|
||||
::: amqtt.contexts.BrokerConfig
|
||||
options:
|
||||
heading_level: 3
|
||||
extra:
|
||||
class_style: "simple"
|
||||
|
||||
::: amqtt.contexts.ListenerConfig
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### Manual docs
|
||||
|
||||
### `listeners` *(list[dict[str, Any]])*
|
||||
|
||||
Defines the network listeners used by the service. Items defined in the `default` listener will be
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{% extends "_base/class.html.jinja" %}
|
||||
|
||||
{% block signature scoped %}
|
||||
{% if config.extra.class_style != 'simple' %}
|
||||
{{ super() }}
|
||||
{% endif %}
|
||||
{% endblock signature %}
|
||||
|
||||
{% block bases scoped %}
|
||||
{% if config.extra.class_style != 'simple' %}
|
||||
{{ super() }}
|
||||
{% endif %}
|
||||
{% endblock bases %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/attribute.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">attribute.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/backlinks.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">backlinks.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/children.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">children.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/class.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">class.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/admonition.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/admonition.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/attributes.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/attributes.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/classes.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/classes.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/examples.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/examples.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/functions.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/functions.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/modules.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/modules.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/other_parameters.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/other_parameters.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/parameters.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/parameters.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/raises.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/raises.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/receives.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/receives.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/returns.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/returns.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/warns.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/warns.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/docstring/yields.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">docstring/yields.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{% extends "_base/expression.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
|
||||
<< expression.html.jinja >>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/function.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">function.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/labels.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">labels.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/language.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">language.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/languages/en.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">languages/en.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/languages/ja.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">languages/ja.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/languages/zh.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">languages/zh.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/module.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">module.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/signature.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<< signature.html.jinja >>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/summary.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">summary.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/summary/attributes.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">summary/attributes.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/summary/classes.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">summary/classes.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/summary/functions.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">summary/functions.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "_base/summary/modules.html.jinja" %}
|
||||
|
||||
{% block logs scoped %}
|
||||
<p style="color:red">summary/modules.html.jinja</p>
|
||||
{% endblock logs %}
|
||||
|
||||
|
|
@ -127,10 +127,12 @@ plugins:
|
|||
- coverage
|
||||
- mkdocs-typer2
|
||||
- mkdocstrings:
|
||||
custom_templates: 'docs/templates'
|
||||
handlers:
|
||||
python:
|
||||
paths: [amqtt]
|
||||
options:
|
||||
annotation_path: "full"
|
||||
docstring_options:
|
||||
ignore_init_summary: true
|
||||
docstring_section_style: list
|
||||
|
@ -148,6 +150,8 @@ plugins:
|
|||
show_symbol_type_toc: true
|
||||
signature_crossrefs: true
|
||||
summary: true
|
||||
extensions:
|
||||
|
||||
- git-revision-date-localized:
|
||||
enabled: !ENV [DEPLOY, false]
|
||||
enable_creation_date: true
|
||||
|
|
|
@ -10,88 +10,6 @@ from amqtt.contexts import BrokerConfig, ListenerConfig, Dictable
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@dataclass
|
||||
class NestedData(Dictable):
|
||||
nested_one: int | None = None
|
||||
nested_two: str | None = None
|
||||
nested_three: str | None = "empty option"
|
||||
|
||||
|
||||
@dataclass
|
||||
class MainData(Dictable):
|
||||
main_one: str | None = None
|
||||
main_two: str | None = 2
|
||||
main_three: dict[str, NestedData] | None = field(default_factory=dict)
|
||||
|
||||
|
||||
data_a = {
|
||||
'main_one': 'value main one',
|
||||
'main_three': {
|
||||
'nested_key_one': {
|
||||
'nested_one': 101,
|
||||
'nested_two': 'value nested two'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data_b = {
|
||||
'option_one': 'value ten'
|
||||
}
|
||||
|
||||
data_c = {
|
||||
'main_two': 'value main two',
|
||||
'main_three': {
|
||||
'nested_key_one': {
|
||||
'nested_two': 'other nested two'
|
||||
},
|
||||
'nested_key_two': {
|
||||
'nested_one': 202,
|
||||
'nested_three': 'other nested three'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_mismatched_data():
|
||||
|
||||
with pytest.raises(UnexpectedDataError):
|
||||
_ = from_dict(data_class=MainData, data=data_b, config=Config(cast=[StrEnum], strict=True))
|
||||
|
||||
def test_nested_data():
|
||||
main_a = from_dict(data_class=MainData, data=data_a, config=Config(cast=[StrEnum], strict=True))
|
||||
main_c = from_dict(data_class=MainData, data=data_c, config=Config(cast=[StrEnum], strict=True))
|
||||
|
||||
main_a.update(main_c)
|
||||
|
||||
assert isinstance(main_c, MainData)
|
||||
assert isinstance(main_c, MainData)
|
||||
assert isinstance(main_c.main_three, dict)
|
||||
assert 'nested_key_one' in main_c.main_three
|
||||
assert isinstance(main_c.main_three['nested_key_one'], NestedData)
|
||||
assert 'nested_key_two' in main_c.main_three
|
||||
|
||||
assert main_c.main_three['nested_key_two'].nested_two == 'value nested two'
|
||||
|
||||
assert main_a.main_one == 'value main one'
|
||||
assert main_a.main_two == 'value main two'
|
||||
assert main_a.main_three['nested_key_one'].nested_two == 'other nested two'
|
||||
assert main_a.main_three['nested_key_one'] == 'value twelve'
|
||||
assert main_a.main_three['nested_key_one'] == 'value three'
|
||||
|
||||
|
||||
data = {
|
||||
"listeners": {
|
||||
"default": {
|
||||
"bind": "0.0.0.0:1883",
|
||||
"max_connections": 50
|
||||
},
|
||||
"secure": {
|
||||
"bind": "0.0.0.0:8883",
|
||||
"cafile": "ca.key"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def _test_broker_config():
|
||||
# Parse with dacite
|
||||
|
|
Ładowanie…
Reference in New Issue