gpframe 0.0.2__tar.gz → 0.0.4__tar.gz
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.
- {gpframe-0.0.2/src/gpframe.egg-info → gpframe-0.0.4}/PKG-INFO +1 -1
- {gpframe-0.0.2 → gpframe-0.0.4}/pyproject.toml +1 -1
- gpframe-0.0.4/src/gpframe/__init__.py +142 -0
- gpframe-0.0.4/src/gpframe/api/builder.py +87 -0
- gpframe-0.0.4/src/gpframe/api/contexts.py +79 -0
- gpframe-0.0.4/src/gpframe/api/frame.py +50 -0
- gpframe-0.0.4/src/gpframe/api/outcome.py +23 -0
- gpframe-0.0.4/src/gpframe/impl/builder.py +364 -0
- gpframe-0.0.4/src/gpframe/impl/context/event.py +51 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/context/routine.py +8 -3
- gpframe-0.0.4/src/gpframe/impl/frame.py +56 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/handler/event.py +3 -2
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/handler/exception.py +9 -7
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/handler/redo.py +7 -5
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/handler/terminated.py +7 -8
- gpframe-0.0.4/src/gpframe/impl/lifecycle/__init__.py +8 -0
- gpframe-0.0.4/src/gpframe/impl/lifecycle/mod.py +133 -0
- gpframe-0.0.4/src/gpframe/impl/lifecycle/phase.py +133 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/outcome.py +0 -25
- gpframe-0.0.2/src/gpframe/impl/execute/ansynchronize.py → gpframe-0.0.4/src/gpframe/impl/routine/asynchronous.py +2 -2
- {gpframe-0.0.2/src/gpframe/impl/execute → gpframe-0.0.4/src/gpframe/impl/routine}/base.py +4 -3
- {gpframe-0.0.2/src/gpframe/impl → gpframe-0.0.4/src/gpframe/impl/routine}/result.py +10 -8
- {gpframe-0.0.2/src/gpframe/impl/execute → gpframe-0.0.4/src/gpframe/impl/routine}/subprocess.py +2 -2
- gpframe-0.0.2/src/gpframe/impl/execute/synchronize.py → gpframe-0.0.4/src/gpframe/impl/routine/synchronous.py +2 -2
- {gpframe-0.0.2 → gpframe-0.0.4/src/gpframe.egg-info}/PKG-INFO +1 -1
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe.egg-info/SOURCES.txt +14 -12
- gpframe-0.0.2/src/gpframe/__init__.py +0 -65
- gpframe-0.0.2/src/gpframe/api/contexts.py +0 -220
- gpframe-0.0.2/src/gpframe/api/gpframe.py +0 -284
- gpframe-0.0.2/src/gpframe/api/outcome.py +0 -59
- gpframe-0.0.2/src/gpframe/api/result.py +0 -41
- gpframe-0.0.2/src/gpframe/impl/context/controller.py +0 -56
- gpframe-0.0.2/src/gpframe/impl/context/event.py +0 -50
- gpframe-0.0.2/src/gpframe/impl/frame.py +0 -492
- gpframe-0.0.2/src/gpframe/impl/state.py +0 -173
- {gpframe-0.0.2 → gpframe-0.0.4}/LICENSE +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/README.md +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/setup.cfg +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/api/__init__.py +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/__init__.py +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/context/__init__.py +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/errors.py +0 -0
- {gpframe-0.0.2/src/gpframe/impl/execute → gpframe-0.0.4/src/gpframe/impl/handler}/__init__.py +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/handler/errors.py +0 -0
- {gpframe-0.0.2/src/gpframe/impl/handler → gpframe-0.0.4/src/gpframe/impl/routine}/__init__.py +0 -0
- {gpframe-0.0.2/src/gpframe/impl/execute → gpframe-0.0.4/src/gpframe/impl/routine}/errors.py +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe/impl/syncm.py +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe.egg-info/dependency_links.txt +0 -0
- {gpframe-0.0.2 → gpframe-0.0.4}/src/gpframe.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gpframe"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "A general-purpose framework for routine execution with concurrency and event handling."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gpframe: A general-purpose framework for routine execution with concurrency and event handling.
|
|
3
|
+
------------
|
|
4
|
+
|
|
5
|
+
Note
|
|
6
|
+
---------------
|
|
7
|
+
This library is in an early stage of development.
|
|
8
|
+
It is generally unstable: documentation and tests are incomplete,
|
|
9
|
+
and the API may undergo breaking changes without notice.
|
|
10
|
+
|
|
11
|
+
Core Components
|
|
12
|
+
---------------
|
|
13
|
+
- FrameBuilder (from .api.builder):
|
|
14
|
+
Factory function to obtain a FrameBuilderType.
|
|
15
|
+
Accepts a routine to be executed inside a Frame.
|
|
16
|
+
The routine can be synchronous or asynchronous.
|
|
17
|
+
|
|
18
|
+
- FrameBuilderType (from .api.builder):
|
|
19
|
+
API to configure the Frame name, logger, and handlers.
|
|
20
|
+
Calling FrameBuilderType.start() launches the Frame and returns a Frame object.
|
|
21
|
+
|
|
22
|
+
- Frame (from .api.frame):
|
|
23
|
+
Represents the entire lifecycle of a Frame.
|
|
24
|
+
Provides Frame.task for accessing the Frame's execution task.
|
|
25
|
+
Allows writing to the request message map via Frame.request.update().
|
|
26
|
+
|
|
27
|
+
- EventContext (from .api.contexts):
|
|
28
|
+
Allows writing to the event_message map through EventContext.event_message.update().
|
|
29
|
+
|
|
30
|
+
- RoutineContext (from .api.contexts):
|
|
31
|
+
Allows writing to the routine_message map through RoutineContext.routine_message.update().
|
|
32
|
+
|
|
33
|
+
- Outcome (from .api.outcome):
|
|
34
|
+
Passed to the terminated_callback after the Frame has finished (after on_close).
|
|
35
|
+
Provides read-only access to all message maps.
|
|
36
|
+
Each message map is a snapshot of its state at termination.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
Message Maps
|
|
40
|
+
------------
|
|
41
|
+
gpframe defines four synchronized message maps and one result container.
|
|
42
|
+
These maps are the core mechanism for communication between the Frame,
|
|
43
|
+
the routine, and event handlers.
|
|
44
|
+
|
|
45
|
+
1. environment
|
|
46
|
+
- Purpose: Immutable configuration or contextual information.
|
|
47
|
+
- Access:
|
|
48
|
+
Read: Frame, EventContext, RoutineContext
|
|
49
|
+
Write: Frame (setup stage only)
|
|
50
|
+
- Example: constants, system configuration, resource identifiers.
|
|
51
|
+
|
|
52
|
+
2. request
|
|
53
|
+
- Purpose: Incoming requests or instructions that affect routine behavior.
|
|
54
|
+
- Access:
|
|
55
|
+
Read: Frame, EventContext, RoutineContext
|
|
56
|
+
Write: Frame (via Frame.request.update())
|
|
57
|
+
- Example: runtime parameters, control flags.
|
|
58
|
+
|
|
59
|
+
3. event_message
|
|
60
|
+
- Purpose: Event-driven updates produced by event handlers.
|
|
61
|
+
- Access:
|
|
62
|
+
Read: Frame, RoutineContext
|
|
63
|
+
Write: EventContext (via EventContext.event_message.update())
|
|
64
|
+
- Example: status events, log signals, external notifications.
|
|
65
|
+
|
|
66
|
+
4. routine_message
|
|
67
|
+
- Purpose: Communication channel from the routine to other components.
|
|
68
|
+
- Access:
|
|
69
|
+
Read: Frame, EventContext
|
|
70
|
+
Write: RoutineContext (via RoutineContext.routine_message.update())
|
|
71
|
+
- Example: progress reports, intermediate results.
|
|
72
|
+
|
|
73
|
+
5. routine_result
|
|
74
|
+
- Purpose: Result value of the routine execution.
|
|
75
|
+
- Access:
|
|
76
|
+
Read: EventContext.routine_result
|
|
77
|
+
Write: set internally by each routine execution
|
|
78
|
+
- Behavior:
|
|
79
|
+
Updated every time the routine finishes (success, error, or cancellation).
|
|
80
|
+
After the Frame terminates, the last value represents the final outcome.
|
|
81
|
+
- Special values:
|
|
82
|
+
NO_VALUE = not yet executed or failed
|
|
83
|
+
|
|
84
|
+
Lifecycle and Access Rules
|
|
85
|
+
--------------------------
|
|
86
|
+
- All maps are thread-safe (backed by SynchronizedMapReader/SynchronizedMapUpdater).
|
|
87
|
+
- During Frame execution, access is restricted according to context type.
|
|
88
|
+
- After Frame termination, direct access is invalid and raises TerminatedError.
|
|
89
|
+
To inspect final state, use Outcome which contains a snapshot of all maps.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
Error Handling
|
|
93
|
+
--------------
|
|
94
|
+
- NO_VALUE (from .impl.routine.result):
|
|
95
|
+
Initial value of EventContext.routine_result.value.
|
|
96
|
+
Indicates that no routine result exists (not yet executed, exception raised, etc.).
|
|
97
|
+
|
|
98
|
+
- TerminatedError (from .impl.builder):
|
|
99
|
+
Raised when attempting to access message maps after a Frame has terminated.
|
|
100
|
+
In such cases, maps must be accessed via Outcome.
|
|
101
|
+
|
|
102
|
+
- FutureTimeoutError, ThreadCleanupTimeoutError (from .impl.routine.asynchronous):
|
|
103
|
+
Timeout-related errors for asynchronous routines and thread cleanup.
|
|
104
|
+
|
|
105
|
+
- SubprocessTimeoutError (from .impl.routine.subprocess):
|
|
106
|
+
Timeout error for subprocess routines.
|
|
107
|
+
|
|
108
|
+
- Throw (from .impl.handler.exception):
|
|
109
|
+
Exception wrapper for re-throwing errors without being wrapped as HandlerError.
|
|
110
|
+
Useful when propagating exceptions such as asyncio.CancelledError directly.
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
from .api.builder import FrameBuilder
|
|
115
|
+
|
|
116
|
+
from .api.builder import FrameBuilderType
|
|
117
|
+
|
|
118
|
+
from .api.frame import Frame
|
|
119
|
+
|
|
120
|
+
from .api.contexts import EventContext
|
|
121
|
+
from .api.contexts import RoutineContext
|
|
122
|
+
|
|
123
|
+
from .api.outcome import Outcome
|
|
124
|
+
|
|
125
|
+
from .impl.routine.result import NO_VALUE
|
|
126
|
+
|
|
127
|
+
from .impl.builder import TerminatedError
|
|
128
|
+
|
|
129
|
+
from .impl.routine.asynchronous import FutureTimeoutError, ThreadCleanupTimeoutError
|
|
130
|
+
from .impl.routine.subprocess import SubprocessTimeoutError
|
|
131
|
+
|
|
132
|
+
from .impl.handler.exception import Throw
|
|
133
|
+
|
|
134
|
+
__all__ = ("FrameBuilder", "FrameBuilderType",
|
|
135
|
+
"Frame",
|
|
136
|
+
"EventContext", "RoutineContext",
|
|
137
|
+
"Outcome",
|
|
138
|
+
"NO_VALUE",
|
|
139
|
+
"TerminatedError",
|
|
140
|
+
"FutureTimeoutError", "ThreadCleanupTimeoutError",
|
|
141
|
+
"SubprocessTimeoutError",
|
|
142
|
+
"Throw")
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
6
|
+
|
|
7
|
+
R = TypeVar("R")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
from ..impl.frame import Frame
|
|
14
|
+
from ..impl.builder import Routine
|
|
15
|
+
from ..impl.handler.event import EventHandler
|
|
16
|
+
from ..impl.handler.redo import RedoHandler
|
|
17
|
+
from ..impl.handler.exception import ExceptionHandler
|
|
18
|
+
from ..impl.handler.terminated import TerminatedHandler
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def FrameBuilder(routine: Routine[R]) -> FrameBuilderType[R]:
|
|
22
|
+
from ..impl.builder import create_builder_role
|
|
23
|
+
role = create_builder_role(routine)
|
|
24
|
+
return role.interface
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FrameBuilderType(ABC, Generic[R]):
|
|
28
|
+
__slots__ = ()
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def set_name(self, name: str) -> None:
|
|
31
|
+
...
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def set_logger(self, logger: logging.Logger):
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def set_environments(self, environments: dict):
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def set_requests(self, requests: dict):
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def set_routine_timeout(self, timeout: float | None) -> None:
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def set_cleanup_timeout(self, timeout: float | None) -> None:
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def start(self, *, as_subprocess: bool = False) -> Frame:
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def set_terminated_callback(self, handler: TerminatedHandler):
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def set_exception_handler(self, handler: ExceptionHandler):
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def set_redo_handler(self, handler: RedoHandler[R]) -> None:
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def set_on_open(self, handler: EventHandler[R]) -> None:
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def set_on_start(self, handler: EventHandler[R]) -> None:
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def set_on_end(self, handler: EventHandler[R]) -> None:
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def set_on_cancel(self, handler: EventHandler[R]) -> None:
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def set_on_close(self, handler: EventHandler[R]) -> None:
|
|
87
|
+
...
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
R = TypeVar("R")
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from logging import Logger
|
|
10
|
+
|
|
11
|
+
from ..impl.routine.result import RoutineResult
|
|
12
|
+
from ..impl.syncm import SynchronizedMapReader, SynchronizedMapUpdater
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EventContext(ABC, Generic[R]):
|
|
16
|
+
__slots__ = ()
|
|
17
|
+
@property
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def frame_name(self) -> str:
|
|
20
|
+
...
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def logger(self) -> Logger:
|
|
24
|
+
...
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def routine_in_subprocess(self) -> bool:
|
|
28
|
+
...
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def environment(self) -> SynchronizedMapReader:
|
|
32
|
+
...
|
|
33
|
+
@property
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def request(self) -> SynchronizedMapReader:
|
|
36
|
+
...
|
|
37
|
+
@property
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def event_message(self) -> SynchronizedMapUpdater:
|
|
40
|
+
...
|
|
41
|
+
@property
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def routine_message(self) -> SynchronizedMapReader:
|
|
44
|
+
...
|
|
45
|
+
@property
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def routine_result(self) -> RoutineResult[R]:
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
class RoutineContext(ABC):
|
|
51
|
+
__slots__ = ()
|
|
52
|
+
@property
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def frame_name(self) -> str:
|
|
55
|
+
...
|
|
56
|
+
@property
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def logger_name(self) -> str:
|
|
59
|
+
...
|
|
60
|
+
@property
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def routine_in_subprocess(self) -> bool:
|
|
63
|
+
...
|
|
64
|
+
@property
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def environment(self) -> SynchronizedMapReader:
|
|
67
|
+
...
|
|
68
|
+
@property
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def request(self) -> SynchronizedMapReader:
|
|
71
|
+
...
|
|
72
|
+
@property
|
|
73
|
+
@abstractmethod
|
|
74
|
+
def event_message(self) -> SynchronizedMapReader:
|
|
75
|
+
...
|
|
76
|
+
@property
|
|
77
|
+
@abstractmethod
|
|
78
|
+
def routine_message(self) -> SynchronizedMapUpdater:
|
|
79
|
+
...
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
import asyncio
|
|
5
|
+
from logging import Logger
|
|
6
|
+
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..impl.syncm import SynchronizedMapReader, SynchronizedMapUpdater
|
|
10
|
+
|
|
11
|
+
class Frame(ABC):
|
|
12
|
+
__slots__ = ()
|
|
13
|
+
@property
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def frame_name(self) -> str:
|
|
16
|
+
...
|
|
17
|
+
@property
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def logger(self) -> Logger:
|
|
20
|
+
...
|
|
21
|
+
@property
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def routine_in_subprocess(self) -> bool:
|
|
24
|
+
...
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def environment(self) -> SynchronizedMapReader:
|
|
28
|
+
...
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def request(self) -> SynchronizedMapUpdater:
|
|
32
|
+
...
|
|
33
|
+
@property
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def event_message(self) -> SynchronizedMapReader:
|
|
36
|
+
...
|
|
37
|
+
@property
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def routine_message(self) -> SynchronizedMapReader:
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def request_stop_routine(self, *, kill: bool = False) -> None:
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def task(self) -> asyncio.Task:
|
|
49
|
+
...
|
|
50
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
|
|
9
|
+
class Outcome(ABC):
|
|
10
|
+
__slots__ = ()
|
|
11
|
+
@property
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def requests(self) -> MappingProxyType:
|
|
14
|
+
...
|
|
15
|
+
@property
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def event_messages(self) -> MappingProxyType:
|
|
18
|
+
...
|
|
19
|
+
@property
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def routine_messages(self) -> MappingProxyType:
|
|
22
|
+
...
|
|
23
|
+
|