redzed 25.12.30__py3-none-any.whl → 26.2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- redzed/__init__.py +4 -4
- redzed/base_block.py +2 -2
- redzed/block.py +18 -19
- redzed/blocklib/counter.py +4 -0
- redzed/blocklib/fsm.py +81 -52
- redzed/blocklib/inputs.py +38 -66
- redzed/blocklib/outputs.py +109 -77
- redzed/blocklib/repeat.py +29 -13
- redzed/blocklib/timedate.py +3 -3
- redzed/blocklib/timeinterval.py +4 -0
- redzed/blocklib/timer.py +3 -4
- redzed/blocklib/validator.py +46 -0
- redzed/circuit.py +65 -40
- redzed/cron_service.py +137 -123
- redzed/debug.py +70 -53
- redzed/formula_trigger.py +4 -8
- redzed/initializers.py +6 -7
- redzed/py.typed +0 -0
- redzed/signal_shutdown.py +3 -3
- redzed/undef.py +2 -2
- redzed/utils/async_utils.py +1 -100
- redzed/utils/data_utils.py +20 -44
- {redzed-25.12.30.dist-info → redzed-26.2.4.dist-info}/METADATA +2 -2
- redzed-26.2.4.dist-info/RECORD +30 -0
- {redzed-25.12.30.dist-info → redzed-26.2.4.dist-info}/WHEEL +1 -1
- {redzed-25.12.30.dist-info → redzed-26.2.4.dist-info}/licenses/LICENSE.txt +1 -1
- redzed-25.12.30.dist-info/RECORD +0 -28
- {redzed-25.12.30.dist-info → redzed-26.2.4.dist-info}/top_level.txt +0 -0
redzed/debug.py
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Debug
|
|
2
|
+
Debug levels.
|
|
3
|
+
- - - - - -
|
|
4
|
+
Part of the redzed package.
|
|
5
|
+
Docs: https://redzed.readthedocs.io/en/latest/
|
|
6
|
+
Home: https://github.com/xitop/redzed/
|
|
3
7
|
"""
|
|
4
8
|
from __future__ import annotations
|
|
5
9
|
|
|
6
|
-
__all__ = ['get_debug_level', 'set_debug_level']
|
|
7
|
-
|
|
8
10
|
import logging
|
|
9
11
|
import os
|
|
10
12
|
|
|
11
13
|
from . import circuit
|
|
12
14
|
|
|
15
|
+
__all__ = ['get_debug_level', 'set_debug_level']
|
|
16
|
+
|
|
13
17
|
_logger = logging.getLogger(__package__)
|
|
14
|
-
_debug_handler: logging.StreamHandler|None = None
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
def get_level_from_env() -> int|None:
|
|
21
|
+
if (env_level := os.environ.get('REDZED_DEBUG', '')) == '':
|
|
22
|
+
return None
|
|
23
|
+
try:
|
|
24
|
+
level = int(env_level)
|
|
25
|
+
except Exception:
|
|
26
|
+
pass
|
|
27
|
+
else:
|
|
28
|
+
if 0 <= level <= 3:
|
|
29
|
+
return level
|
|
30
|
+
_logger.warning(
|
|
31
|
+
"Envvar 'REDZED_DEBUG' should be: 0 (disabled), 1 (normal), "
|
|
32
|
+
+ "2 (verbose) or 3 (verbose with circuit timestamps)")
|
|
33
|
+
_logger.error("Ignoring REDZED_DEBUG='%s'. Please use a correct value.", env_level)
|
|
34
|
+
return None
|
|
24
35
|
|
|
25
36
|
|
|
26
37
|
class _DebugLevel:
|
|
@@ -31,56 +42,62 @@ class _DebugLevel:
|
|
|
31
42
|
"""
|
|
32
43
|
|
|
33
44
|
def __init__(self) -> None:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return 0
|
|
42
|
-
level = level.strip()
|
|
43
|
-
if not level:
|
|
44
|
-
return 0
|
|
45
|
-
if level in {'0', '1', '2', '3'}:
|
|
46
|
-
return int(level)
|
|
47
|
-
_logger.warning(
|
|
48
|
-
"Envvar 'REDZED_DEBUG' should be: 0 (disabled), 1 (normal), "
|
|
49
|
-
+ "2 (verbose) or 3 (verbose with circuit timestamps)")
|
|
50
|
-
_logger.warning("Ignoring REDZED_DEBUG='%s'. Please use a correct value.", level)
|
|
51
|
-
return 0
|
|
52
|
-
|
|
53
|
-
def get_level(self) -> int:
|
|
45
|
+
if (level := get_level_from_env()) is None:
|
|
46
|
+
level = 0
|
|
47
|
+
self._level = level
|
|
48
|
+
_logger.debug("[Logging] Debug level: %d", level)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def level(self) -> int:
|
|
54
52
|
return self._level
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
@level.setter
|
|
55
|
+
def level(self, level: int) -> None:
|
|
57
56
|
"""Set debug level."""
|
|
58
|
-
global _debug_handler # pylint: disable=global-statement
|
|
59
|
-
|
|
60
|
-
if not isinstance(level, int):
|
|
61
|
-
raise TypeError(f"Expected an integer, got {level!r}")
|
|
62
57
|
if level == self._level:
|
|
63
58
|
return
|
|
59
|
+
if not isinstance(level, int):
|
|
60
|
+
raise TypeError(f"Expected an integer, got {level!r}")
|
|
64
61
|
if not 0 <= level <= 3:
|
|
65
62
|
raise ValueError(f"Debug level must be an integer 0 to 3, but got {level}")
|
|
66
|
-
|
|
67
|
-
if self._level >= 0:
|
|
68
|
-
_logger.debug("Debug level: %d -> %d", self._level, level)
|
|
69
|
-
elif level > 0:
|
|
70
|
-
_logger.debug("Debug level: %d", level)
|
|
63
|
+
_logger.debug("[Logging] Debug level: %d -> %d", self._level, level)
|
|
71
64
|
self._level = level
|
|
72
|
-
if level == 0:
|
|
73
|
-
if _debug_handler is not None:
|
|
74
|
-
_logger.removeHandler(_debug_handler)
|
|
75
|
-
_debug_handler = None
|
|
76
|
-
_logger.setLevel(logging.WARNING)
|
|
77
|
-
elif not _logger.hasHandlers() and _debug_handler is None:
|
|
78
|
-
_logger.addHandler(_debug_handler := logging.StreamHandler())
|
|
79
|
-
_debug_handler.setFormatter(_CircuitTimeFormatter("%(levelname)s - %(message)s"))
|
|
80
|
-
_logger.setLevel(logging.DEBUG)
|
|
81
65
|
|
|
82
66
|
|
|
83
|
-
|
|
67
|
+
_debug_level = _DebugLevel()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_debug_level():
|
|
71
|
+
"""Get the debug level."""
|
|
72
|
+
return _debug_level.level
|
|
84
73
|
|
|
85
|
-
|
|
86
|
-
set_debug_level
|
|
74
|
+
|
|
75
|
+
def set_debug_level(level):
|
|
76
|
+
"""
|
|
77
|
+
Set the debug level.
|
|
78
|
+
|
|
79
|
+
Also make sure debug messages will be logged or printed.
|
|
80
|
+
For this purpose, if there is no handler, add an own stream handler.
|
|
81
|
+
"""
|
|
82
|
+
_debug_level.level = level
|
|
83
|
+
if circuit.get_circuit().get_state() in [
|
|
84
|
+
circuit.CircuitState.UNDER_CONSTRUCTION, circuit.CircuitState.CLOSED]:
|
|
85
|
+
return
|
|
86
|
+
if level > 0:
|
|
87
|
+
if not _logger.hasHandlers():
|
|
88
|
+
_logger.addHandler(logging.StreamHandler())
|
|
89
|
+
_logger.propagate = False
|
|
90
|
+
_logger.setLevel(logging.DEBUG)
|
|
91
|
+
else:
|
|
92
|
+
_logger.setLevel(logging.NOTSET if _logger.propagate else logging.INFO)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class _CircuitTimeFilter(logging.Filter):
|
|
96
|
+
"""Filter adding timestamsps in debug level 3."""
|
|
97
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
98
|
+
if _debug_level.level >= 3 and isinstance(record.msg, str):
|
|
99
|
+
record.msg = f"{circuit.get_circuit().runtime():.03f} {record.msg}"
|
|
100
|
+
return True
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
_logger.addFilter(_CircuitTimeFilter())
|
redzed/formula_trigger.py
CHANGED
|
@@ -194,12 +194,8 @@ class Formula(base_block.BlockOrFormula):
|
|
|
194
194
|
self._evaluate_active = False
|
|
195
195
|
return triggers
|
|
196
196
|
|
|
197
|
-
def formula(
|
|
197
|
+
def formula(func: _FUNC) -> _FUNC:
|
|
198
198
|
"""@formula() creates a Formula block with the decorated function."""
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def decorator(func: _FUNC) -> _FUNC:
|
|
203
|
-
Formula(name, *args, func=func, **kwargs)
|
|
204
|
-
return func
|
|
205
|
-
return decorator
|
|
199
|
+
comment = '' if func.__doc__ is None else inspect.cleandoc(func.__doc__).partition('\n')[0]
|
|
200
|
+
Formula(func.__name__, func=func, comment=comment)
|
|
201
|
+
return func
|
redzed/initializers.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Block initializers.
|
|
3
3
|
- - - - - -
|
|
4
4
|
Part of the redzed package.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Docs: https://redzed.readthedocs.io/en/latest/
|
|
6
|
+
Project home: https://github.com/xitop/redzed/
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
@@ -18,7 +18,7 @@ import typing as t
|
|
|
18
18
|
|
|
19
19
|
from . import block
|
|
20
20
|
from .undef import UNDEF, UndefType
|
|
21
|
-
from .utils import
|
|
21
|
+
from .utils import time_period
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
_LONG_TIMEOUT = 60 # threshold for a "long timeout" debug message
|
|
@@ -225,19 +225,18 @@ class InitTask(AsyncInitializer):
|
|
|
225
225
|
|
|
226
226
|
def __init__(
|
|
227
227
|
self,
|
|
228
|
-
|
|
228
|
+
aw_func: Callable[..., Awaitable[t.Any]],
|
|
229
229
|
*args: t.Any,
|
|
230
230
|
timeout: float|str = 10.0
|
|
231
231
|
) -> None:
|
|
232
232
|
"""Similar to InitFunction, but asynchonous."""
|
|
233
|
-
check_async_func(coro_func)
|
|
234
233
|
super().__init__(timeout)
|
|
235
|
-
self.
|
|
234
|
+
self._aw_func = aw_func
|
|
236
235
|
self._args = args
|
|
237
236
|
|
|
238
237
|
async def _async_get_init(self) -> t.Any:
|
|
239
238
|
async with asyncio.timeout(self._timeout):
|
|
240
|
-
return await self.
|
|
239
|
+
return await self._aw_func(*self._args)
|
|
241
240
|
|
|
242
241
|
|
|
243
242
|
class InitWait(AsyncInitializer):
|
redzed/py.typed
ADDED
|
File without changes
|
redzed/signal_shutdown.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Stop the runner with a signal.
|
|
3
3
|
- - - - - -
|
|
4
4
|
Part of the redzed package.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Docs: https://redzed.readthedocs.io/en/latest/
|
|
6
|
+
Project home: https://github.com/xitop/redzed/
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
@@ -55,7 +55,7 @@ class TerminatingSignal:
|
|
|
55
55
|
"""A signal handler."""
|
|
56
56
|
# - we need the _threadsafe variant of call_soon
|
|
57
57
|
# - get_running loop() and get_circuit() will succeed,
|
|
58
|
-
# because this handler is active only during
|
|
58
|
+
# because this handler is active only during redzed.run()
|
|
59
59
|
msg = f"Signal {self._signame!r} caught"
|
|
60
60
|
call_soon = asyncio.get_running_loop().call_soon_threadsafe
|
|
61
61
|
call_soon(_logger.warning, "%s", msg)
|
redzed/undef.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
The UNDEF singleton constant.
|
|
3
3
|
- - - - - -
|
|
4
4
|
Part of the redzed package.
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
Docs: https://redzed.readthedocs.io/en/latest/
|
|
6
|
+
Home: https://github.com/xitop/redzed/
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
redzed/utils/async_utils.py
CHANGED
|
@@ -8,7 +8,7 @@ import asyncio
|
|
|
8
8
|
from collections.abc import Awaitable
|
|
9
9
|
import typing as t
|
|
10
10
|
|
|
11
|
-
__all__ = ['BufferShutDown', 'cancel_shield'
|
|
11
|
+
__all__ = ['BufferShutDown', 'cancel_shield']
|
|
12
12
|
|
|
13
13
|
try:
|
|
14
14
|
BufferShutDown = asyncio.QueueShutDown # Python 3.13+
|
|
@@ -18,105 +18,6 @@ except AttributeError:
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
_T = t.TypeVar("_T")
|
|
21
|
-
_MISSING = object()
|
|
22
|
-
|
|
23
|
-
class MsgSync(t.Generic[_T]):
|
|
24
|
-
"""
|
|
25
|
-
Task synchronization based on sending messages of type _T.
|
|
26
|
-
|
|
27
|
-
The count of unread message is always either zero or one,
|
|
28
|
-
because messages are not queued. A new message replaces
|
|
29
|
-
the previous unread one, if any.
|
|
30
|
-
|
|
31
|
-
An existing unread message is available immediately.
|
|
32
|
-
Otherwise the recipient must wait and will be awakened when
|
|
33
|
-
a new message arrives. Reading a message consumes it,
|
|
34
|
-
so each message can reach only one receiver.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
def __init__(self) -> None:
|
|
38
|
-
self._msg: t.Any = _MISSING
|
|
39
|
-
self._shutdown = False
|
|
40
|
-
self._has_data = asyncio.Event() # valid before shutdown
|
|
41
|
-
self._draining = False # valid after shutdown
|
|
42
|
-
|
|
43
|
-
def shutdown(self) -> None:
|
|
44
|
-
"""
|
|
45
|
-
Disallow sending immediately. Disallow receiving after the MsgSync gets empty.
|
|
46
|
-
|
|
47
|
-
Calls to .send() will raise BufferShutDown.
|
|
48
|
-
|
|
49
|
-
If a message was sent before shutdown and is waiting, it can be
|
|
50
|
-
normally received before the receiver shuts down too. After
|
|
51
|
-
the shutdown all blocked callers will be unblocked with BufferShutDown.
|
|
52
|
-
Calls to .recv() will raise BufferShutDown as well.
|
|
53
|
-
"""
|
|
54
|
-
self._shutdown = True
|
|
55
|
-
self._draining = self._has_data.is_set()
|
|
56
|
-
if not self._draining:
|
|
57
|
-
# wakeup all waiters
|
|
58
|
-
self._has_data.set()
|
|
59
|
-
|
|
60
|
-
def is_shutdown(self) -> bool:
|
|
61
|
-
"""Check if the buffer was shut down."""
|
|
62
|
-
return self._shutdown
|
|
63
|
-
|
|
64
|
-
def send(self, msg: _T) -> None:
|
|
65
|
-
"""
|
|
66
|
-
Send a message to one receiver.
|
|
67
|
-
|
|
68
|
-
If the previously sent message hasn't been received yet,
|
|
69
|
-
the new message overwrites it.
|
|
70
|
-
"""
|
|
71
|
-
if self._shutdown:
|
|
72
|
-
raise BufferShutDown("The buffer was shut down")
|
|
73
|
-
self._msg = msg
|
|
74
|
-
self._has_data.set()
|
|
75
|
-
|
|
76
|
-
def clear(self) -> None:
|
|
77
|
-
"""Remove the waiting message."""
|
|
78
|
-
if self._shutdown:
|
|
79
|
-
self._draining = False
|
|
80
|
-
else:
|
|
81
|
-
self._has_data.clear()
|
|
82
|
-
self._msg = _MISSING
|
|
83
|
-
|
|
84
|
-
def has_data(self) -> bool:
|
|
85
|
-
"""Return True only if there is a waiting message."""
|
|
86
|
-
return self._draining if self._shutdown else self._has_data.is_set()
|
|
87
|
-
|
|
88
|
-
async def _recv(self) -> _T:
|
|
89
|
-
"""Receive (consume) a message."""
|
|
90
|
-
while not self._has_data.is_set():
|
|
91
|
-
await self._has_data.wait()
|
|
92
|
-
if not self._shutdown:
|
|
93
|
-
self._has_data.clear()
|
|
94
|
-
elif self._draining:
|
|
95
|
-
self._draining = False
|
|
96
|
-
else:
|
|
97
|
-
raise BufferShutDown("The buffer was shut down")
|
|
98
|
-
msg, self._msg = self._msg, _MISSING
|
|
99
|
-
return t.cast(_T, msg)
|
|
100
|
-
|
|
101
|
-
async def recv(self, timeout: float | None = None, default: t.Any = _MISSING) -> t.Any:
|
|
102
|
-
"""
|
|
103
|
-
Receive a message with optional timeout.
|
|
104
|
-
|
|
105
|
-
If a message is not available, wait until it arrives.
|
|
106
|
-
|
|
107
|
-
If a timeout [seconds] is given, return the default value
|
|
108
|
-
if no message is received before the timeout period elapses.
|
|
109
|
-
Without a default, raise the TimeoutError.
|
|
110
|
-
"""
|
|
111
|
-
if timeout is None:
|
|
112
|
-
return await self._recv()
|
|
113
|
-
try:
|
|
114
|
-
async with asyncio.timeout(timeout):
|
|
115
|
-
return await self._recv()
|
|
116
|
-
except TimeoutError:
|
|
117
|
-
if default is not _MISSING:
|
|
118
|
-
return default
|
|
119
|
-
raise
|
|
120
21
|
|
|
121
22
|
|
|
122
23
|
async def cancel_shield(aw: Awaitable[_T]) -> _T:
|
redzed/utils/data_utils.py
CHANGED
|
@@ -4,12 +4,11 @@ Small utilities.
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
|
-
'
|
|
7
|
+
'check_identifier', 'func_name', 'func_call_string',
|
|
8
8
|
'is_multiple', 'tasks_are_eager', 'to_tuple']
|
|
9
9
|
|
|
10
10
|
import asyncio
|
|
11
11
|
from collections.abc import Callable, Mapping, Sequence
|
|
12
|
-
import inspect
|
|
13
12
|
import itertools
|
|
14
13
|
import logging
|
|
15
14
|
import typing as t
|
|
@@ -24,7 +23,7 @@ def check_identifier(name: t.Any, msg_prefix: str) -> None:
|
|
|
24
23
|
if not name:
|
|
25
24
|
raise ValueError(f"{msg_prefix} cannot be an empty string")
|
|
26
25
|
if not name.isidentifier():
|
|
27
|
-
raise ValueError(f"{msg_prefix} must be a valid identifier, got '{name
|
|
26
|
+
raise ValueError(f"{msg_prefix} must be a valid identifier, got '{name}'")
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
def is_multiple(arg: t.Any) -> bool:
|
|
@@ -50,19 +49,29 @@ def to_tuple(args: _T_item|Sequence[_T_item]) -> tuple[_T_item, ...]:
|
|
|
50
49
|
return t.cast(tuple[_T_item], (args,))
|
|
51
50
|
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
def func_name(func: Callable[..., t.Any]) -> str:
|
|
53
|
+
"""Return the name of a callable."""
|
|
54
|
+
if not callable(func):
|
|
55
|
+
raise TypeError(f"{func!r} is not callable")
|
|
56
|
+
if (name := getattr(func, '__name__', None)) is not None:
|
|
57
|
+
return name
|
|
58
|
+
# callable objects with __call__ do not have a __name__
|
|
59
|
+
if not isinstance(func, type) and (hasattr(ftype := type(func), '__call__')):
|
|
60
|
+
return f'{ftype.__name__}.__call__'
|
|
61
|
+
# fail-safe default, though there is no such type of callable
|
|
62
|
+
return ftype.__name__
|
|
63
|
+
|
|
64
|
+
|
|
54
65
|
def func_call_string(
|
|
55
66
|
func: Callable[..., t.Any]|None,
|
|
56
67
|
args: Sequence[t.Any],
|
|
57
|
-
kwargs: Mapping[str, t.Any] =
|
|
68
|
+
kwargs: Mapping[str, t.Any]|None = None
|
|
58
69
|
) -> str:
|
|
59
70
|
"""Convert args and kwargs to a printable string."""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if func is None
|
|
64
|
-
return arglist
|
|
65
|
-
return func.__name__ + arglist
|
|
71
|
+
agen = (repr(a) for a in args)
|
|
72
|
+
gen = itertools.chain(agen, (f"{k}={v!r}" for k, v in kwargs.items())) if kwargs else agen
|
|
73
|
+
arglist = f"({', '.join(gen)})"
|
|
74
|
+
return arglist if func is None else func_name(func) + arglist
|
|
66
75
|
|
|
67
76
|
|
|
68
77
|
def tasks_are_eager() -> bool:
|
|
@@ -81,36 +90,3 @@ def tasks_are_eager() -> bool:
|
|
|
81
90
|
flag = True
|
|
82
91
|
asyncio.create_task(test_task())
|
|
83
92
|
return flag
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def check_async_coro(arg: t.Any) -> None:
|
|
87
|
-
"""
|
|
88
|
-
Check if arg is a coroutine object.
|
|
89
|
-
|
|
90
|
-
Raise a TypeError with a descriptive message if it isn't.
|
|
91
|
-
"""
|
|
92
|
-
if inspect.iscoroutine(arg):
|
|
93
|
-
return
|
|
94
|
-
if inspect.iscoroutinefunction(arg):
|
|
95
|
-
received = f"an async function '{arg.__name__}'. Did you mean '{arg.__name__}()'?"
|
|
96
|
-
else:
|
|
97
|
-
received = repr(arg)
|
|
98
|
-
raise TypeError(f"Expected a coroutine, but got {received}")
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def check_async_func(arg: t.Any) -> None:
|
|
102
|
-
"""
|
|
103
|
-
Check if *arg* is an async function.
|
|
104
|
-
|
|
105
|
-
Raise a TypeError with a descriptive message if it isn't.
|
|
106
|
-
"""
|
|
107
|
-
if inspect.iscoroutinefunction(arg):
|
|
108
|
-
return
|
|
109
|
-
if inspect.iscoroutine(arg):
|
|
110
|
-
received = (f"a coroutine '{arg.__name__}()'. "
|
|
111
|
-
+ f"Did you mean '{arg.__name__}' without parentheses?")
|
|
112
|
-
elif callable(arg):
|
|
113
|
-
received = f"a non-async function/callable '{arg.__name__}'"
|
|
114
|
-
else:
|
|
115
|
-
received = repr(arg)
|
|
116
|
-
raise TypeError(f"Expected an async function, but got {received}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redzed
|
|
3
|
-
Version:
|
|
3
|
+
Version: 26.2.4
|
|
4
4
|
Summary: An asyncio-based library for building small automated systems
|
|
5
5
|
Author-email: Vlado Potisk <redzed@poti.sk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -8,7 +8,7 @@ Project-URL: homepage, https://github.com/xitop/redzed
|
|
|
8
8
|
Project-URL: repository, https://github.com/xitop/redzed
|
|
9
9
|
Project-URL: documentation, https://redzed.readthedocs.io/en/latest/
|
|
10
10
|
Keywords: automation,finite-state machine
|
|
11
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
redzed/__init__.py,sha256=IVRc3KnMDB9N_M0fiii8X84I6_AEjhmOqtrzAJbvuzY,1097
|
|
2
|
+
redzed/base_block.py,sha256=A1ZxEIH8jsNXj3z3HvzgAVshUsMoFtqIoR41-U2vMaw,5187
|
|
3
|
+
redzed/block.py,sha256=_Qp8DQ1Ojb_PuGcYy2xS4gkhUeQymIx69iJaKHUM9jQ,11768
|
|
4
|
+
redzed/circuit.py,sha256=6C1mmg83Z_mjCHUpLlU9mdK4297cjrBycIuUUnjSB5E,31894
|
|
5
|
+
redzed/cron_service.py,sha256=0RSIte6zkbF4mSojrPoVFMHkVep1OZ2Mv2BtG3YiPTs,11372
|
|
6
|
+
redzed/debug.py,sha256=hsHbBxPOOUVL_utfiRnkRh49FszDvHsCZ9xAqxawUIU,2868
|
|
7
|
+
redzed/formula_trigger.py,sha256=lkBMeLVrcvqA71WGGapj9hitm52-dATMgT5QFhC1lOY,7360
|
|
8
|
+
redzed/initializers.py,sha256=1vqnxvQTRYJunAOmYMuZAOL9sr1GPP9Wz2FK92o4wVc,7740
|
|
9
|
+
redzed/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
redzed/signal_shutdown.py,sha256=BAolHuMw-P86R8cSIebjsvUDZOSt-PanKFA0iQS1GIc,2078
|
|
11
|
+
redzed/undef.py,sha256=0Q70QdtbmCHbAmFwJJ7L8CILi1-hxYOKU5nlR5EyH9I,892
|
|
12
|
+
redzed/blocklib/__init__.py,sha256=au7jbjO_RUFzK5U4STk-7pc1DTVA0OVh2W_aKsx1Y3s,557
|
|
13
|
+
redzed/blocklib/counter.py,sha256=pUT5Np31gk1rP2LqaTjJdzdyyS8NhpD9104Re_MtLpY,1251
|
|
14
|
+
redzed/blocklib/fsm.py,sha256=3UVQyKwZstL96jf3HYfuvN6R49uOMXVo20MFz9EpGkc,24746
|
|
15
|
+
redzed/blocklib/inputs.py,sha256=YtKtbDUtXyQyUTNadniwyxCA_-UPCgrrY17EipfXcaI,6223
|
|
16
|
+
redzed/blocklib/outputs.py,sha256=77FwFzh3q1Ri7tsAi39fmojD0vMmwBTSAYEjf1ZABpg,13964
|
|
17
|
+
redzed/blocklib/repeat.py,sha256=R4f0WjTTnqpiYOHDCHucmGdp6ChyVozLaZDaE-Nx7Tc,3146
|
|
18
|
+
redzed/blocklib/timedate.py,sha256=bmYZx6ToYsuD3UQzc1zxD2ddvovzF0A63-y4Pnkrm6Q,5225
|
|
19
|
+
redzed/blocklib/timeinterval.py,sha256=alK68glDdWPD9SWxZ97pEzhQIReC41RMdXrRIRZsyuE,6878
|
|
20
|
+
redzed/blocklib/timer.py,sha256=fCQTbmBOqo7pc6k32yj7JoyUM9XbdgHOXzu2tRiOiJE,1483
|
|
21
|
+
redzed/blocklib/validator.py,sha256=6r0qtPIJ2neCP32WhI7tGxEUus_ji6kx_jrzHd2_M9I,1312
|
|
22
|
+
redzed/utils/__init__.py,sha256=Yo8cj1f1HQj862UOdCsXOkMg4kfq1c5S3HJDhuiE5os,326
|
|
23
|
+
redzed/utils/async_utils.py,sha256=ABwloG7YKqRwfIwWsXNaGlNPc7MdEdq29-aRGRDsBgk,1075
|
|
24
|
+
redzed/utils/data_utils.py,sha256=hesExhPcM2-IZPbDXAuGCUMpCI4YiNodp_ZJUj0_h9I,2952
|
|
25
|
+
redzed/utils/time_utils.py,sha256=eCqk4T4ipn4hgzL8-4Usn1W6kkTyRCuQ9-BMSvnELww,8524
|
|
26
|
+
redzed-26.2.4.dist-info/licenses/LICENSE.txt,sha256=brj9B7uNdzUvTJON_5Eibf7zeLRhcMuBYMInalu0KlI,1091
|
|
27
|
+
redzed-26.2.4.dist-info/METADATA,sha256=nQARn1_dngD-r1UBeYDVthqAwmqy6taWhJhDlP14kNo,2055
|
|
28
|
+
redzed-26.2.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
29
|
+
redzed-26.2.4.dist-info/top_level.txt,sha256=7Rt0BRMqaJ0AGAmrd2JDqmqSY4cmQeW--7u6KDV1gZg,7
|
|
30
|
+
redzed-26.2.4.dist-info/RECORD,,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025 Vlado Potisk <redzed@poti.sk>
|
|
3
|
+
Copyright (c) 2025-2026 Vlado Potisk <redzed@poti.sk>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
redzed-25.12.30.dist-info/RECORD
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
redzed/__init__.py,sha256=eyvuX8qwxEFuiP6jOu4x1qv-AmHJAGKOziV1dKCNWWA,1098
|
|
2
|
-
redzed/base_block.py,sha256=NzJhY0POAJUyfp9EcQG2RvXM472vAV5iWG5LdsScpso,5191
|
|
3
|
-
redzed/block.py,sha256=0iwK8Pb5LJ0xzoOt5dkx-FeWplzUzAWHOWfah368u7s,12012
|
|
4
|
-
redzed/circuit.py,sha256=VkcAZZg5HfEdboUZ_vMnElI9E4MprwaoBveN8-hUvZw,31095
|
|
5
|
-
redzed/cron_service.py,sha256=f-YR0w2U0dpipX271cyUYXvsvJunpE3mywuV7SkFE_k,10581
|
|
6
|
-
redzed/debug.py,sha256=QeWHpfTZADPB6nY0zrj1Aqo91Wd9HB9bAJVgq_d3hCo,2767
|
|
7
|
-
redzed/formula_trigger.py,sha256=0_TVbR1wCQ0Wv7NlJtcfD-iDUbmTBlbVCSdbKDw_tX0,7516
|
|
8
|
-
redzed/initializers.py,sha256=oF5Wkdu2REFdfuCEdo8kS-9N2uW2dhdzYWoibILB3ks,7804
|
|
9
|
-
redzed/signal_shutdown.py,sha256=KjL6Kq9K0m_2nerjo7hryNFPhgdNTmSyTOfDi-SMsjA,2081
|
|
10
|
-
redzed/undef.py,sha256=iPxcUIKBDh5wqjdr8qvs9EQkx5s52ryNDY22at-PF0c,896
|
|
11
|
-
redzed/blocklib/__init__.py,sha256=au7jbjO_RUFzK5U4STk-7pc1DTVA0OVh2W_aKsx1Y3s,557
|
|
12
|
-
redzed/blocklib/counter.py,sha256=XvJPOarg6RZ9H-GrWFQrt3JJnGk9YGc1B7NOpF6ju4A,1125
|
|
13
|
-
redzed/blocklib/fsm.py,sha256=6Qv4pOsF-FOEBm9NQUSD6F2yLfb0chvbpvbrgg3sIk8,23492
|
|
14
|
-
redzed/blocklib/inputs.py,sha256=hhfU2Id7oJfmhZB-_PoowXBfYDuw9OQyceg_AprJVVs,7218
|
|
15
|
-
redzed/blocklib/outputs.py,sha256=hoBPr_978PyhR6OzD0rxou34PFukIRD1pIpK6JNgqQE,12894
|
|
16
|
-
redzed/blocklib/repeat.py,sha256=hkIO7qgm4wS32dcKkkfL_DjnoKWf-l3k2DfRiRUk6iE,2500
|
|
17
|
-
redzed/blocklib/timedate.py,sha256=78wqiH1mUz5zoz3ZgDOmsPaYYmQLypgt9IfmLUcH9Cw,5196
|
|
18
|
-
redzed/blocklib/timeinterval.py,sha256=hxPhao1Gm9q_NOp6WdjmsStkrfwkadH5uJh-Hli9D-4,6752
|
|
19
|
-
redzed/blocklib/timer.py,sha256=X3CoqwGJ46oLw0R5u0HK1a11VpcZdTWvPbWOkFjZrDI,1520
|
|
20
|
-
redzed/utils/__init__.py,sha256=Yo8cj1f1HQj862UOdCsXOkMg4kfq1c5S3HJDhuiE5os,326
|
|
21
|
-
redzed/utils/async_utils.py,sha256=Vknijh9rABL8GcJbYv6a7I4qO2VLerMsGTr5wiGWbjo,4486
|
|
22
|
-
redzed/utils/data_utils.py,sha256=xiaVnEJF7Q9oGFqqTOyiVyzI-sG0lYHxImfyFcXjwFI,3542
|
|
23
|
-
redzed/utils/time_utils.py,sha256=eCqk4T4ipn4hgzL8-4Usn1W6kkTyRCuQ9-BMSvnELww,8524
|
|
24
|
-
redzed-25.12.30.dist-info/licenses/LICENSE.txt,sha256=HwuPxdAOIKlSWHESVtk6Zov7d0BZaPu74eruoZc5UN8,1086
|
|
25
|
-
redzed-25.12.30.dist-info/METADATA,sha256=LiyTs38Xa48jh2ozWNw1uUICaNKrTg7xWSUd1vEHTK0,2058
|
|
26
|
-
redzed-25.12.30.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
27
|
-
redzed-25.12.30.dist-info/top_level.txt,sha256=7Rt0BRMqaJ0AGAmrd2JDqmqSY4cmQeW--7u6KDV1gZg,7
|
|
28
|
-
redzed-25.12.30.dist-info/RECORD,,
|
|
File without changes
|