gpframe 0.0.1__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.
Files changed (33) hide show
  1. gpframe-0.0.1/LICENSE +21 -0
  2. gpframe-0.0.1/PKG-INFO +48 -0
  3. gpframe-0.0.1/README.md +8 -0
  4. gpframe-0.0.1/pyproject.toml +52 -0
  5. gpframe-0.0.1/setup.cfg +4 -0
  6. gpframe-0.0.1/src/gpframe/__init__.py +57 -0
  7. gpframe-0.0.1/src/gpframe/api/__init__.py +0 -0
  8. gpframe-0.0.1/src/gpframe/api/contexts.py +220 -0
  9. gpframe-0.0.1/src/gpframe/api/error.py +52 -0
  10. gpframe-0.0.1/src/gpframe/api/gpframe.py +330 -0
  11. gpframe-0.0.1/src/gpframe/api/outcome.py +59 -0
  12. gpframe-0.0.1/src/gpframe/api/result.py +23 -0
  13. gpframe-0.0.1/src/gpframe/impl/__init__.py +0 -0
  14. gpframe-0.0.1/src/gpframe/impl/context/__init__.py +0 -0
  15. gpframe-0.0.1/src/gpframe/impl/context/controller.py +59 -0
  16. gpframe-0.0.1/src/gpframe/impl/context/event.py +50 -0
  17. gpframe-0.0.1/src/gpframe/impl/context/routine.py +55 -0
  18. gpframe-0.0.1/src/gpframe/impl/frame.py +579 -0
  19. gpframe-0.0.1/src/gpframe/impl/handler/__init__.py +0 -0
  20. gpframe-0.0.1/src/gpframe/impl/handler/error.py +6 -0
  21. gpframe-0.0.1/src/gpframe/impl/handler/event.py +40 -0
  22. gpframe-0.0.1/src/gpframe/impl/handler/exception.py +53 -0
  23. gpframe-0.0.1/src/gpframe/impl/handler/redo.py +41 -0
  24. gpframe-0.0.1/src/gpframe/impl/handler/terminated.py +44 -0
  25. gpframe-0.0.1/src/gpframe/impl/outcome.py +67 -0
  26. gpframe-0.0.1/src/gpframe/impl/result.py +69 -0
  27. gpframe-0.0.1/src/gpframe/impl/state.py +173 -0
  28. gpframe-0.0.1/src/gpframe/impl/stop.py +137 -0
  29. gpframe-0.0.1/src/gpframe/impl/syncm.py +141 -0
  30. gpframe-0.0.1/src/gpframe.egg-info/PKG-INFO +48 -0
  31. gpframe-0.0.1/src/gpframe.egg-info/SOURCES.txt +31 -0
  32. gpframe-0.0.1/src/gpframe.egg-info/dependency_links.txt +1 -0
  33. gpframe-0.0.1/src/gpframe.egg-info/top_level.txt +1 -0
gpframe-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 minoru
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
gpframe-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: gpframe
3
+ Version: 0.0.1
4
+ Summary: A general-purpose framework for routine execution with concurrency and event handling.
5
+ Author: minoru_jp
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 minoru
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/minoru-jp/gpframe
28
+ Project-URL: Source, https://github.com/minoru-jp/gpframe
29
+ Project-URL: Issues, https://github.com/minoru-jp/gpframe/issues
30
+ Keywords: general-purpose,framework,concurrency,async,subprocess,event,lifecycle,context,outcome,exception-handling,wrapper,skeleton,task,workflow,execution,state-management
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
36
+ Requires-Python: >=3.10
37
+ Description-Content-Type: text/markdown
38
+ License-File: LICENSE
39
+ Dynamic: license-file
40
+
41
+ # gpframe
42
+
43
+ A general-purpose framework for managing routine execution with concurrency, event handling, and lifecycle control.
44
+
45
+ Note: This project is in an early stage. It currently provides implementation only, with no tests yet.
46
+
47
+ [![PyPI version](https://img.shields.io/pypi/v/gpframe.svg)](https://pypi.org/project/gpframe/)
48
+
@@ -0,0 +1,8 @@
1
+ # gpframe
2
+
3
+ A general-purpose framework for managing routine execution with concurrency, event handling, and lifecycle control.
4
+
5
+ Note: This project is in an early stage. It currently provides implementation only, with no tests yet.
6
+
7
+ [![PyPI version](https://img.shields.io/pypi/v/gpframe.svg)](https://pypi.org/project/gpframe/)
8
+
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["setuptools>=70", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gpframe"
7
+ version = "0.0.1"
8
+ description = "A general-purpose framework for routine execution with concurrency and event handling."
9
+ readme = { file = "README.md", content-type = "text/markdown" }
10
+ requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
+ authors = [
13
+ { name = "minoru_jp" }
14
+ ]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+ keywords = [
23
+ "general-purpose",
24
+ "framework",
25
+ "concurrency",
26
+ "async",
27
+ "subprocess",
28
+ "event",
29
+ "lifecycle",
30
+ "context",
31
+ "outcome",
32
+ "exception-handling",
33
+ "wrapper",
34
+ "skeleton",
35
+ "task",
36
+ "workflow",
37
+ "execution",
38
+ "state-management"
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/minoru-jp/gpframe"
43
+ Source = "https://github.com/minoru-jp/gpframe"
44
+ Issues = "https://github.com/minoru-jp/gpframe/issues"
45
+
46
+
47
+ [tool.setuptools]
48
+ package-dir = {"" = "src"}
49
+
50
+ [tool.setuptools.packages.find]
51
+ where = ["src"]
52
+ include = ["gpframe*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,57 @@
1
+ """
2
+ Unified routine execution framework with frames, events, and contexts.
3
+
4
+ gpframe wraps user-defined routines with a *frame* that provides lifecycle
5
+ management, event handling, re-execution, and shared state. The routine itself
6
+ can remain a simple function or async function, while the frame coordinates
7
+ control flow and consistency.
8
+
9
+ Key features:
10
+ - Works generically, from simple processes to event-driven extensions
11
+ and parallel execution control
12
+ - Supports both synchronous and asynchronous routines
13
+ - Synchronous routines can optionally be executed in subprocesses
14
+ (asynchronous ones are excluded)
15
+
16
+ Execution flow:
17
+ - on_start → routine execution → on_end → redo check (continue or finish)
18
+ - on_cancel is called when canceled
19
+ - on_close is always called upon termination, followed by the terminated callback
20
+
21
+ Data and error handling:
22
+ - Results, errors, and message exchange are consolidated into an ``Outcome``
23
+ - Exceptions from routines, frames, and event handlers are distinguished,
24
+ and which to re-raise is controlled by ``Raises``
25
+ - Shared data (environment, requests, event messages) is managed with
26
+ synchronized maps to ensure consistency across contexts
27
+ - Contexts (``ControllerContext``, ``EventContext``, ``RoutineContext``)
28
+ provide safe and structured access during execution
29
+
30
+ Main components:
31
+ - **Frame** — entry point that wraps and manages routines
32
+ - **ControllerContext / EventContext / RoutineContext** — execution contexts
33
+ - **Outcome** — holds results, errors, requests, and messages
34
+ - **Raises** — selects which exception types to re-raise
35
+ - **Error types** — ``FrameError``, ``TerminatedError``, ``NotTerminatedError``, etc.
36
+
37
+ gpframe provides unified control features without burdening the routine itself.
38
+ """
39
+ from .api.gpframe import Frame
40
+
41
+ from .api.gpframe import FrameError, FrameType, TerminatedError, NotTerminatedError
42
+ from .api.error import Raises
43
+
44
+ from .api.contexts import Controller
45
+ from .api.contexts import EventContext
46
+ from .api.contexts import RoutineContext
47
+
48
+ from .api.outcome import Outcome
49
+
50
+ from .api.result import NO_VALUE
51
+
52
+
53
+ __all__ = ("Frame", "FrameType",
54
+ "FrameError", "TerminatedError", "NotTerminatedError", "Raises",
55
+ "Controller", "EventContext", "RoutineContext", "Outcome",
56
+ "NO_VALUE")
57
+
File without changes
@@ -0,0 +1,220 @@
1
+ """
2
+ contexts.py
3
+
4
+ Defines abstract context interfaces for controllers, event handlers,
5
+ and routines. These contexts provide structured access to logging,
6
+ shared state, and communication channels, making it clear which
7
+ elements are read-only and which are writable.
8
+
9
+ Design notes:
10
+ - All contexts separate read-only and writable elements.
11
+ - Logging is always available.
12
+ - Shared state and message access are structured to prevent
13
+ unintended interference.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from abc import ABC, abstractmethod
18
+ from typing import TYPE_CHECKING
19
+
20
+ if TYPE_CHECKING:
21
+ from logging import Logger
22
+
23
+ from ..impl.result import RoutineResult
24
+ from ..impl.syncm import SynchronizedMapReader, SynchronizedMapUpdater
25
+
26
+
27
+ class Controller(ABC):
28
+ """Controller for running routine.
29
+
30
+ Provides:
31
+ - Read-only access to environment and inbound messages
32
+ - Writable request channel
33
+ - Routine result access
34
+ - Lifecycle control (stop / is_running)
35
+
36
+ Notes:
37
+ - `environment`, `event_msg`, `routine_msg`, `routine_result` are read-only
38
+ - `request` is the only writable channel
39
+ - `stop()` is idempotent
40
+ """
41
+ __slots__ = ()
42
+ @property
43
+ @abstractmethod
44
+ def frame_name(self) -> str:
45
+ ...
46
+ @property
47
+ @abstractmethod
48
+ def logger(self) -> Logger:
49
+ ...
50
+ @property
51
+ @abstractmethod
52
+ def routine_in_subprocess(self) -> bool:
53
+ ...
54
+ @property
55
+ @abstractmethod
56
+ def environment(self) -> SynchronizedMapReader:
57
+ ...
58
+ @property
59
+ @abstractmethod
60
+ def request(self) -> SynchronizedMapUpdater:
61
+ """Writable channel to send requests to the routine.
62
+
63
+ This is a best-effort communication mechanism:
64
+ - The routine may use these requests as hints or instructions.
65
+ - There is no guarantee that any given request will be executed
66
+ or even recognized.
67
+ - Actual behavior depends on the routine's implementation.
68
+
69
+ Use this as a way to suggest actions, not as a strict control channel.
70
+ """
71
+ @property
72
+ @abstractmethod
73
+ def event_msg(self) -> SynchronizedMapReader:
74
+ ...
75
+ @property
76
+ @abstractmethod
77
+ def routine_msg(self) -> SynchronizedMapReader:
78
+ ...
79
+ @property
80
+ @abstractmethod
81
+ def routine_result(self) -> RoutineResult:
82
+ """Result container for the routine.
83
+
84
+ Notes:
85
+ - The `RoutineResult.value` field is initialized to `NO_VALUE`.
86
+ - This remains `NO_VALUE` if the routine has not yet executed,
87
+ or if it finished without producing a result.
88
+ - Compare against `NO_VALUE` to detect these cases.
89
+ """
90
+ @abstractmethod
91
+ def stop(self, *, kill: bool = False) -> None:
92
+ """Request the routine to stop.
93
+
94
+ Behavior depends on how the routine is executed:
95
+ - Async routine: calls `cancel()`. If `kill=True`, the flag is ignored.
96
+ - Subprocess routine: calls `terminate()`. If `kill=True`, calls `kill()` instead.
97
+ - Synchronous in-process routine: not supported → raises TypeError.
98
+
99
+ Args:
100
+ kill: If True and the routine is a subprocess, use a hard kill instead
101
+ of graceful termination. Ignored for async routines.
102
+
103
+ Raises:
104
+ TypeError: If the routine is a synchronous in-process function.
105
+ """
106
+ @property
107
+ @abstractmethod
108
+ def routine_is_running(self) -> bool:
109
+ """Whether the routine appears to be running.
110
+
111
+ Notes:
112
+ - This value is an approximate indicator only.
113
+ - It may not reflect the exact, real-time execution state.
114
+ - Use it as a guideline rather than a strict guarantee.
115
+ """
116
+
117
+
118
+
119
+ class EventContext(ABC):
120
+ """Context used inside event handlers.
121
+
122
+ Provides:
123
+ - Read-only access to environment, request, routine messages, and results
124
+ - Writable event message channel
125
+
126
+ Notes:
127
+ - `request` is read-only here (no sending)
128
+ - `event_msg` is the only writable channel
129
+ """
130
+ __slots__ = ()
131
+ @property
132
+ @abstractmethod
133
+ def frame_name(self) -> str:
134
+ ...
135
+ @property
136
+ @abstractmethod
137
+ def logger(self) -> Logger:
138
+ ...
139
+ @property
140
+ @abstractmethod
141
+ def routine_in_subprocess(self) -> bool:
142
+ ...
143
+ @property
144
+ @abstractmethod
145
+ def environment(self) -> SynchronizedMapReader:
146
+ ...
147
+ @property
148
+ @abstractmethod
149
+ def request(self) -> SynchronizedMapReader:
150
+ ...
151
+ @property
152
+ @abstractmethod
153
+ def event_msg(self) -> SynchronizedMapUpdater:
154
+ ...
155
+ @property
156
+ @abstractmethod
157
+ def routine_msg(self) -> SynchronizedMapReader:
158
+ ...
159
+ @property
160
+ @abstractmethod
161
+ def routine_result(self) -> RoutineResult:
162
+ """Result container for the routine.
163
+
164
+ Notes:
165
+ - The `RoutineResult.value` field is initialized to `NO_VALUE`.
166
+ - This remains `NO_VALUE` if the routine has not yet executed,
167
+ or if it finished without producing a result.
168
+ - Compare against `NO_VALUE` to detect these cases.
169
+ """
170
+
171
+
172
+ class RoutineContext(ABC):
173
+ """Context for routines executed by the system.
174
+
175
+ Provides:
176
+ - Read-only access to environment, request, and event messages
177
+ - Writable routine message channel
178
+ - Logger name and subprocess execution flag
179
+
180
+ Notes:
181
+ - When `executes_as_subprocess` is True, data must be serializable
182
+ - `routine_msg` is the only writable channel
183
+ """
184
+ __slots__ = ()
185
+ @property
186
+ @abstractmethod
187
+ def frame_name(self) -> str:
188
+ ...
189
+ @property
190
+ @abstractmethod
191
+ def logger_name(self) -> str:
192
+ """Name of the logger channel associated with this routine.
193
+
194
+ Unlike other contexts, a `Logger` instance is not provided directly.
195
+ Instead, use this name to obtain a logger when needed.
196
+
197
+ Reason:
198
+ - Supports routines that may run in a separate subprocess,
199
+ where direct logger objects cannot be shared safely.
200
+ """
201
+ @property
202
+ @abstractmethod
203
+ def routine_in_subprocess(self) -> bool:
204
+ ...
205
+ @property
206
+ @abstractmethod
207
+ def environment(self) -> SynchronizedMapReader:
208
+ ...
209
+ @property
210
+ @abstractmethod
211
+ def request(self) -> SynchronizedMapUpdater:
212
+ ...
213
+ @property
214
+ @abstractmethod
215
+ def event_msg(self) -> SynchronizedMapReader:
216
+ ...
217
+ @property
218
+ @abstractmethod
219
+ def routine_msg(self) -> SynchronizedMapReader:
220
+ ...
@@ -0,0 +1,52 @@
1
+ """
2
+ Exception handling policy for gpframe.
3
+
4
+ Defines the ``Raises`` enumeration, an ``IntFlag`` used to
5
+ specify which categories of exceptions should be re-raised.
6
+
7
+ Notes:
8
+ - Members can be combined with bitwise operations.
9
+ - ``Raises.ALL`` is equivalent to combining all flags.
10
+ """
11
+
12
+ from enum import IntFlag, auto
13
+
14
+ class Raises(IntFlag):
15
+ """
16
+ Enumeration of flags that control which exceptions are re-raised.
17
+
18
+ Members:
19
+ SUPPRESS
20
+ Suppress all exceptions.
21
+ ROUTINE
22
+ Re-raise exceptions raised in routines.
23
+ FRAME
24
+ Re-raise exceptions raised during frame control.
25
+ HANDLER
26
+ Re-raise exceptions raised in handlers.
27
+ ALL
28
+ Equivalent to combining ROUTINE, FRAME, and HANDLER.
29
+ """
30
+ SUPPRESS = 0
31
+ ROUTINE = auto()
32
+ FRAME = auto()
33
+ HANDLER = auto()
34
+ ALL = ROUTINE | FRAME | HANDLER
35
+
36
+ def matches(self, r: Exception | None, f: Exception | None, h: Exception | None) -> bool:
37
+ """
38
+ Check whether any of the given exceptions should be re-raised.
39
+
40
+ Args:
41
+ r: Exception from a routine, or None if none occurred.
42
+ f: Exception from frame control, or None if none occurred.
43
+ h: Exception from a handler, or None if none occurred.
44
+
45
+ Returns:
46
+ True if at least one exception matches this Raises setting,
47
+ otherwise False.
48
+ """
49
+ result = r is not None and self & Raises.ROUTINE
50
+ result |= f is not None and self & Raises.FRAME
51
+ result |= h is not None and self & Raises.HANDLER
52
+ return bool(result)