domubus 0.1.0__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.
@@ -0,0 +1,34 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ *.egg
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ ENV/
16
+
17
+ # IDE
18
+ .idea/
19
+ .vscode/
20
+ *.swp
21
+ *.swo
22
+
23
+ # Testing
24
+ .coverage
25
+ htmlcov/
26
+ .pytest_cache/
27
+ coverage.xml
28
+
29
+ # mypy
30
+ .mypy_cache/
31
+
32
+ # ruff
33
+ .ruff_cache/
34
+
domubus-0.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 voxDomus Team
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.
22
+
domubus-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: domubus
3
+ Version: 0.1.0
4
+ Summary: Async/sync event bus with optional Pydantic integration, persistence, and priority handlers
5
+ Project-URL: Homepage, https://github.com/lensator/domubus
6
+ Project-URL: Documentation, https://github.com/lensator/domubus#readme
7
+ Project-URL: Repository, https://github.com/lensator/domubus
8
+ Project-URL: Issues, https://github.com/lensator/domubus/issues
9
+ Author: voxDomus Team
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: async,event-bus,event-driven,home-automation,pubsub,pydantic
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Home Automation
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Provides-Extra: dev
28
+ Requires-Dist: mypy>=1.0; extra == 'dev'
29
+ Requires-Dist: pydantic>=2.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
32
+ Requires-Dist: pytest>=7.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
34
+ Provides-Extra: pydantic
35
+ Requires-Dist: pydantic>=2.0; extra == 'pydantic'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # domubus
39
+
40
+ A type-safe, async/sync event bus for Python with optional Pydantic integration, persistence, and priority handlers. **Zero required dependencies.**
41
+
42
+ ## Features
43
+
44
+ - ✅ **Async & Sync** - Both async and sync handlers, emit from either context
45
+ - ✅ **Zero Dependencies** - Core package uses only Python stdlib
46
+ - ✅ **Optional Pydantic** - Type-safe events when Pydantic is installed
47
+ - ✅ **Priority Handlers** - Control execution order with priority values
48
+ - ✅ **Wildcard Subscriptions** - Subscribe to all events with `*`
49
+ - ✅ **One-time Handlers** - Auto-unsubscribe after first execution
50
+ - ✅ **Event Filters** - Conditionally execute handlers
51
+ - ✅ **JSONL Persistence** - WAL-style event persistence
52
+ - ✅ **Fully Typed** - PEP 561 compatible with `py.typed` marker
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install domubus
58
+
59
+ # With Pydantic support
60
+ pip install domubus[pydantic]
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ```python
66
+ import asyncio
67
+ from domubus import EventBus
68
+
69
+ bus = EventBus()
70
+
71
+ @bus.on("device.light.on")
72
+ async def handle_light_on(event):
73
+ print(f"Light turned on: {event.data}")
74
+
75
+ @bus.on("device.light.on", priority=100)
76
+ def high_priority_handler(event):
77
+ print("This runs first (sync handler)")
78
+
79
+ async def main():
80
+ await bus.emit_async("device.light.on", {"brightness": 100})
81
+
82
+ asyncio.run(main())
83
+ ```
84
+
85
+ ## Typed Events with Pydantic
86
+
87
+ ```python
88
+ from typing import ClassVar
89
+ from domubus import EventBus, BaseEvent
90
+
91
+ class DeviceStateChanged(BaseEvent):
92
+ event_type: ClassVar[str] = "device.state.changed"
93
+ device_id: str
94
+ new_state: str
95
+
96
+ bus = EventBus()
97
+
98
+ @bus.on("device.state.changed")
99
+ async def handle_state_change(event: DeviceStateChanged):
100
+ print(f"Device {event.device_id} -> {event.new_state}")
101
+
102
+ await bus.emit_async(DeviceStateChanged(device_id="light1", new_state="on"))
103
+ ```
104
+
105
+ ## Persistence
106
+
107
+ ```python
108
+ from domubus import EventBus
109
+
110
+ # Events are persisted to JSONL file
111
+ async with EventBus(persistence_path="~/.myapp/events.jsonl") as bus:
112
+ await bus.emit_async("user.login", {"user_id": "123"})
113
+
114
+ # History is automatically loaded on context enter
115
+ history = bus.get_history(event_type="user.login")
116
+ ```
117
+
118
+ ## Wildcard Subscriptions
119
+
120
+ ```python
121
+ @bus.on("*")
122
+ def log_all_events(event):
123
+ print(f"Event: {event.event_type}")
124
+ ```
125
+
126
+ ## One-time Handlers
127
+
128
+ ```python
129
+ @bus.once("system.ready")
130
+ async def on_ready(event):
131
+ print("System ready! (only runs once)")
132
+ ```
133
+
134
+ ## Event Filters
135
+
136
+ ```python
137
+ def only_important(event):
138
+ return event.data.get("priority") == "high"
139
+
140
+ @bus.on("notification", filter_fn=only_important)
141
+ def handle_important(event):
142
+ print(f"Important: {event.data}")
143
+ ```
144
+
145
+ ## Error Handling
146
+
147
+ ```python
148
+ def on_error(exception, event, handler):
149
+ print(f"Handler {handler.__name__} failed: {exception}")
150
+
151
+ bus = EventBus(error_callback=on_error)
152
+ ```
153
+
154
+ ## API Reference
155
+
156
+ ### EventBus
157
+
158
+ - `subscribe(event_type, handler, priority=0, once=False, filter_fn=None)` - Subscribe handler
159
+ - `unsubscribe(handler_id)` - Unsubscribe by ID
160
+ - `on(event_type, ...)` - Decorator for subscribing
161
+ - `once(event_type, ...)` - Decorator for one-time handler
162
+ - `emit_async(event, data=None)` - Emit event asynchronously
163
+ - `emit(event, data=None)` - Emit (async from sync context)
164
+ - `emit_sync(event, data=None)` - Emit synchronously (sync handlers only)
165
+ - `get_history(event_type=None, limit=None)` - Get event history
166
+ - `clear_history()` - Clear in-memory history
167
+ - `clear_handlers()` - Remove all handlers
168
+
169
+ ### Events
170
+
171
+ - `BaseEvent` - Pydantic-based event (or dataclass fallback)
172
+ - `StringEvent` - Simple string-based event with data dict
173
+
174
+ ## License
175
+
176
+ MIT
177
+
@@ -0,0 +1,140 @@
1
+ # domubus
2
+
3
+ A type-safe, async/sync event bus for Python with optional Pydantic integration, persistence, and priority handlers. **Zero required dependencies.**
4
+
5
+ ## Features
6
+
7
+ - ✅ **Async & Sync** - Both async and sync handlers, emit from either context
8
+ - ✅ **Zero Dependencies** - Core package uses only Python stdlib
9
+ - ✅ **Optional Pydantic** - Type-safe events when Pydantic is installed
10
+ - ✅ **Priority Handlers** - Control execution order with priority values
11
+ - ✅ **Wildcard Subscriptions** - Subscribe to all events with `*`
12
+ - ✅ **One-time Handlers** - Auto-unsubscribe after first execution
13
+ - ✅ **Event Filters** - Conditionally execute handlers
14
+ - ✅ **JSONL Persistence** - WAL-style event persistence
15
+ - ✅ **Fully Typed** - PEP 561 compatible with `py.typed` marker
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install domubus
21
+
22
+ # With Pydantic support
23
+ pip install domubus[pydantic]
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ```python
29
+ import asyncio
30
+ from domubus import EventBus
31
+
32
+ bus = EventBus()
33
+
34
+ @bus.on("device.light.on")
35
+ async def handle_light_on(event):
36
+ print(f"Light turned on: {event.data}")
37
+
38
+ @bus.on("device.light.on", priority=100)
39
+ def high_priority_handler(event):
40
+ print("This runs first (sync handler)")
41
+
42
+ async def main():
43
+ await bus.emit_async("device.light.on", {"brightness": 100})
44
+
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ ## Typed Events with Pydantic
49
+
50
+ ```python
51
+ from typing import ClassVar
52
+ from domubus import EventBus, BaseEvent
53
+
54
+ class DeviceStateChanged(BaseEvent):
55
+ event_type: ClassVar[str] = "device.state.changed"
56
+ device_id: str
57
+ new_state: str
58
+
59
+ bus = EventBus()
60
+
61
+ @bus.on("device.state.changed")
62
+ async def handle_state_change(event: DeviceStateChanged):
63
+ print(f"Device {event.device_id} -> {event.new_state}")
64
+
65
+ await bus.emit_async(DeviceStateChanged(device_id="light1", new_state="on"))
66
+ ```
67
+
68
+ ## Persistence
69
+
70
+ ```python
71
+ from domubus import EventBus
72
+
73
+ # Events are persisted to JSONL file
74
+ async with EventBus(persistence_path="~/.myapp/events.jsonl") as bus:
75
+ await bus.emit_async("user.login", {"user_id": "123"})
76
+
77
+ # History is automatically loaded on context enter
78
+ history = bus.get_history(event_type="user.login")
79
+ ```
80
+
81
+ ## Wildcard Subscriptions
82
+
83
+ ```python
84
+ @bus.on("*")
85
+ def log_all_events(event):
86
+ print(f"Event: {event.event_type}")
87
+ ```
88
+
89
+ ## One-time Handlers
90
+
91
+ ```python
92
+ @bus.once("system.ready")
93
+ async def on_ready(event):
94
+ print("System ready! (only runs once)")
95
+ ```
96
+
97
+ ## Event Filters
98
+
99
+ ```python
100
+ def only_important(event):
101
+ return event.data.get("priority") == "high"
102
+
103
+ @bus.on("notification", filter_fn=only_important)
104
+ def handle_important(event):
105
+ print(f"Important: {event.data}")
106
+ ```
107
+
108
+ ## Error Handling
109
+
110
+ ```python
111
+ def on_error(exception, event, handler):
112
+ print(f"Handler {handler.__name__} failed: {exception}")
113
+
114
+ bus = EventBus(error_callback=on_error)
115
+ ```
116
+
117
+ ## API Reference
118
+
119
+ ### EventBus
120
+
121
+ - `subscribe(event_type, handler, priority=0, once=False, filter_fn=None)` - Subscribe handler
122
+ - `unsubscribe(handler_id)` - Unsubscribe by ID
123
+ - `on(event_type, ...)` - Decorator for subscribing
124
+ - `once(event_type, ...)` - Decorator for one-time handler
125
+ - `emit_async(event, data=None)` - Emit event asynchronously
126
+ - `emit(event, data=None)` - Emit (async from sync context)
127
+ - `emit_sync(event, data=None)` - Emit synchronously (sync handlers only)
128
+ - `get_history(event_type=None, limit=None)` - Get event history
129
+ - `clear_history()` - Clear in-memory history
130
+ - `clear_handlers()` - Remove all handlers
131
+
132
+ ### Events
133
+
134
+ - `BaseEvent` - Pydantic-based event (or dataclass fallback)
135
+ - `StringEvent` - Simple string-based event with data dict
136
+
137
+ ## License
138
+
139
+ MIT
140
+
@@ -0,0 +1,67 @@
1
+ """domubus - Async/sync event bus with optional Pydantic integration.
2
+
3
+ A type-safe, async/sync event bus with optional Pydantic integration,
4
+ persistence, and priority handlers. Zero required dependencies.
5
+
6
+ Example:
7
+ from domubus import EventBus, BaseEvent
8
+
9
+ class DeviceStateChanged(BaseEvent):
10
+ event_type = "device.state.changed"
11
+ device_id: str
12
+ new_state: str
13
+
14
+ bus = EventBus()
15
+
16
+ @bus.on("device.state.changed")
17
+ async def handle_state_change(event: DeviceStateChanged):
18
+ print(f"Device {event.device_id} -> {event.new_state}")
19
+
20
+ await bus.emit_async(DeviceStateChanged(device_id="light1", new_state="on"))
21
+ """
22
+
23
+ from domubus.bus import EventBus
24
+ from domubus.events import PYDANTIC_AVAILABLE, BaseEvent, StringEvent
25
+ from domubus.handlers import HandlerEntry, HandlerRegistry
26
+ from domubus.persistence import JSONLPersistence
27
+ from domubus.types import (
28
+ AsyncHandler,
29
+ BaseEventProtocol,
30
+ ErrorCallback,
31
+ EventFilter,
32
+ EventT,
33
+ EventT_co,
34
+ Handler,
35
+ SyncHandler,
36
+ )
37
+ from domubus.watcher import FileWatcher
38
+
39
+ __version__ = "0.1.0"
40
+
41
+ __all__ = [
42
+ "PYDANTIC_AVAILABLE",
43
+ "AsyncHandler",
44
+ # Events
45
+ "BaseEvent",
46
+ "BaseEventProtocol",
47
+ "ErrorCallback",
48
+ # Core
49
+ "EventBus",
50
+ "EventFilter",
51
+ "EventT",
52
+ "EventT_co",
53
+ # Watcher
54
+ "FileWatcher",
55
+ # Types
56
+ "Handler",
57
+ # Handlers
58
+ "HandlerEntry",
59
+ "HandlerRegistry",
60
+ # Persistence
61
+ "JSONLPersistence",
62
+ "StringEvent",
63
+ "SyncHandler",
64
+ # Metadata
65
+ "__version__",
66
+ ]
67
+