Skip to content

Commit cfbd77c

Browse files
committed
Major changes:
- Enhanced API documentation with more detailed explanations and examples - Improved worker thread pattern implementation with better lifecycle management - Added thread-safe property support with notification signals - Enhanced error handling and logging throughout codebase - Implemented graceful shutdown utilities - Added connection type AUTO_CONNECTION for dynamic connection behavior Technical improvements: - Refactored signal/slot connection logic for better thread safety - Enhanced worker thread pattern with proper thread affinity handling - Added context variables for emission tracking - Improved test coverage and organization - Updated code formatting and linting configurations
1 parent 1e17998 commit cfbd77c

35 files changed

+3290
-859
lines changed

README.md

+94-130
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,39 @@
11
# TSignal
22

3-
Are you looking for a signal/slot pattern in Python without Qt dependencies? TSignal provides a lightweight, thread-safe, and asyncio-compatible implementation that gives you all the power of Qt's signal/slot pattern without the heavyweight dependencies. Perfect for:
3+
TSignal is a lightweight, pure-Python signal/slot library that provides thread-safe, asyncio-compatible event handling inspired by the Qt signal/slot pattern—but without the heavyweight Qt dependencies. It enables clean decoupling of components, seamless thread-to-thread communication, and flexible asynchronous/synchronous slot handling.
44

5-
- Async applications needing event handling
6-
- Thread communication in Python applications
7-
- Event-driven architectures
8-
- Decoupled component communication
5+
## Key Features
6+
7+
- **Pure Python**: No Qt or external GUI frameworks needed.
8+
- **Async/Await Friendly**: Slots can be synchronous or asynchronous, and integrate seamlessly with asyncio.
9+
- **Thread-Safe**: Signal emissions and slot executions are automatically managed for thread safety.
10+
- **Flexible Connection Types**: Direct or queued connections, automatically chosen based on the caller and callee threads.
11+
- **Worker Thread Pattern**: Simplify background task execution with a built-in worker pattern that provides an event loop and task queue in a dedicated thread.
12+
- **Familiar Decorators**: Inspired by Qt’s pattern, `@t_with_signals`, `@t_signal`, and `@t_slot` let you define signals and slots declaratively.
913

1014
## Why TSignal?
11-
- 🚀 Pure Python implementation - no Qt or external dependencies required
12-
- ⚡ Async/await support out of the box
13-
- 🔒 Thread-safe signal emission and slot execution
14-
- 🎯 Simple, decorator-based API similar to Qt
15-
- 🔄 Automatic thread handling for cross-thread signals
15+
16+
Modern Python applications often rely on asynchronous operations and multi-threading. Traditional event frameworks either require large external dependencies or lack seamless async/thread support. TSignal provides:
17+
18+
- A minimal, dependency-free solution for event-driven architectures.
19+
- Smooth integration with asyncio for modern async Python code.
20+
- Automatic thread-affinity handling so cross-thread signals "just work."
21+
- Decorator-based API that’s intuitive and maintainable.
22+
23+
## Installation
24+
25+
TSignal requires Python 3.10 or higher.
26+
27+
```bash
28+
git clone https://github.com/TSignalDev/tsignal-python.git
29+
cd tsignal-python
30+
pip install -e .
31+
```
32+
33+
For development (includes tests and linting tools):
34+
```
35+
pip install -e ".[dev]
36+
```
1637

1738
## Quick Start
1839

@@ -46,7 +67,7 @@ counter.count_changed.connect(display, display.on_count_changed)
4667
counter.increment() # Will print: "Count is now: 1"
4768
```
4869

49-
### Async Example
70+
### Asynchronous Slot Example
5071
```python
5172
@t_with_signals
5273
class AsyncDisplay:
@@ -69,51 +90,79 @@ async def main():
6990
asyncio.run(main())
7091
```
7192

72-
## Features
73-
- Requires Python 3.10+
74-
- Easy-to-use signal-slot mechanism with decorators
75-
- Support for both synchronous and asynchronous slots
76-
- Thread-safe signal emissions
77-
- Automatic connection type detection (direct/queued)
78-
- Compatible with Python's asyncio
93+
## Core Concepts
7994

80-
## Installation
95+
### Signals and Slots
96+
- Signals: Declared with `@t_signal`. Signals are attributes of a class that can be emitted to notify interested parties.
97+
- Slots: Declared with `@t_slot`. Slots are methods that respond to signals. Slots can be synchronous or async functions.
98+
- Connections: Use `signal.connect(receiver, slot)` to link signals to slots. Connections can also be made directly to functions or lambdas.
8199

82-
TSignal requires Python 3.10 or higher. You can install it directly from the repository:
100+
### Thread Safety and Connection Types
101+
TSignal automatically detects whether the signal emission and slot execution occur in the same thread or different threads:
83102

84-
```bash
85-
git clone https://github.com/tsignal/tsignal-python.git
86-
cd tsignal-python
87-
pip install -e .
88-
```
103+
- **Direct Connection**: If signal and slot share the same thread affinity, the slot is invoked directly.
104+
- **Queued Connection**: If they differ, the call is queued to the slot’s thread/event loop, ensuring thread safety.
89105

90-
For development installation (includes test dependencies):
91-
```bash
92-
pip install -e ".[dev]"
93-
```
106+
This mechanism frees you from manually dispatching calls across threads.
94107

95-
## Documentation
96-
- [Detailed Usage Guide](docs/usage.md)
97-
- [API Reference](docs/api.md)
98-
- [Examples](docs/examples.md)
99-
- [Logging Guidelines](docs/logging.md)
100-
- [Testing Guide](docs/testing.md)
108+
### Worker Threads
109+
For background work, TSignal provides a `@t_with_worker` decorator that:
101110

102-
## Development
111+
- Spawns a dedicated event loop in a worker thread.
112+
- Allows you to queue async tasks to this worker.
113+
- Enables easy start/stop lifecycle management.
114+
- Integrates with signals and slots for thread-safe updates to the main
103115

104-
### Logging
105-
TSignal uses Python's standard logging module. For detailed logging configuration,
106-
please see [Logging Guidelines](docs/logging.md).
116+
**Worker Example**
117+
```python
118+
from tsignal import t_with_worker, t_signal
119+
120+
@t_with_worker
121+
class DataProcessor:
122+
@t_signal
123+
def processing_done(self):
124+
"""Emitted when processing completes"""
125+
126+
async def run(self, *args, **kwargs):
127+
# The main entry point for the worker thread’s event loop
128+
# Wait for tasks or stopping signal
129+
await self._tsignal_stopping.wait()
130+
131+
async def process_data(self, data):
132+
# Perform heavy computation in the worker thread
133+
result = await heavy_computation(data)
134+
self.processing_done.emit(result)
135+
136+
processor = DataProcessor()
137+
processor.start()
138+
139+
# Queue a task to run in the worker thread:
140+
processor.queue_task(processor.process_data(some_data))
141+
142+
# Stop the worker gracefully
143+
processor.stop()
144+
```
145+
146+
## Documentation and Example
147+
- [Usage Guide](docs/usage.md): Learn how to define signals/slots, manage threads, and structure your event-driven code.
148+
- [API Reference](docs/api.md): Detailed documentation of classes, decorators, and functions.
149+
- [Examples](docs/examples.md): Practical use cases, including UI integration, async operations, and worker pattern usage.
150+
- [Logging Guidelines](docs/logging.md): Configure logging levels and handlers for debugging.
151+
- [Testing Guide](docs/testing.md): earn how to run tests and contribute safely.
152+
153+
## Logging
154+
Configure logging to diagnose issues:
107155

108-
Basic usage:
109156
```python
110157
import logging
111-
logging.getLogger('tsignal').setLevel(logging.INFO)
158+
logging.getLogger('tsignal').setLevel(logging.DEBUG)
112159
```
113160

161+
For more details, see the [Logging Guidelines](docs/logging.md).
162+
114163
## Testing
115164

116-
TSignal includes a comprehensive test suite using pytest. For basic testing:
165+
TSignal uses `pytest` for testing:
117166

118167
```bash
119168
# Run all tests
@@ -126,95 +175,10 @@ pytest -v
126175
pytest tests/unit/test_signal.py
127176
```
128177

129-
For detailed testing instructions and guidelines, see [Testing Guide](docs/testing.md).
178+
See the [Testing Guide](docs/testing.md) for more details.
130179

131180
## Contributing
132-
Please see [Contributing Guidelines](CONTRIBUTING.md) for details on how to contribute to this project.
181+
We welcome contributions. Please read the [Contributing Guidelines](CONTRIBUTING.md) before submitting PRs.
133182

134183
## License
135-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
136-
137-
## Connecting Signals and Slots
138-
139-
### Classic Object-Member Connection
140-
```python
141-
@t_with_signals
142-
class Counter:
143-
@t_signal
144-
def count_changed(self):
145-
pass
146-
147-
@t_with_signals
148-
class Display:
149-
@t_slot
150-
def on_count_changed(self, value):
151-
print(f"Count is now: {value}")
152-
153-
counter = Counter()
154-
display = Display()
155-
counter.count_changed.connect(display, display.on_count_changed)
156-
```
157-
158-
### Function Connection
159-
```python
160-
# Connect to a simple function
161-
def print_value(value):
162-
print(f"Value: {value}")
163-
164-
counter.count_changed.connect(print_value)
165-
166-
# Connect to a lambda
167-
counter.count_changed.connect(lambda x: print(f"Lambda received: {x}"))
168-
169-
# Connect to an object method without @t_slot
170-
class Handler:
171-
def process_value(self, value):
172-
print(f"Processing: {value}")
173-
174-
handler = Handler()
175-
counter.count_changed.connect(handler.process_value)
176-
```
177-
178-
## Worker Thread Pattern
179-
180-
TSignal provides a worker thread pattern that combines thread management with signal/slot communication and task queuing:
181-
182-
```python
183-
from tsignal import t_with_worker
184-
185-
@t_with_worker
186-
class DataProcessor:
187-
async def initialize(self, config=None):
188-
# Setup worker (called in worker thread)
189-
self.config = config or {}
190-
191-
async def process_data(self, data):
192-
# Heavy processing in worker thread
193-
result = await heavy_computation(data)
194-
self.processing_done.emit(result)
195-
196-
async def finalize(self):
197-
# Cleanup worker (called before thread stops)
198-
await self.cleanup()
199-
200-
@t_signal
201-
def processing_done(self):
202-
pass
203-
204-
# Usage
205-
processor = DataProcessor()
206-
processor.start(config={'threads': 4}) # Starts worker thread
207-
208-
# Queue task in worker thread
209-
await processor.queue_task(processor.process_data(some_data))
210-
211-
# Stop worker
212-
processor.stop() # Graceful shutdown
213-
```
214-
215-
The worker pattern provides:
216-
- Dedicated worker thread with event loop
217-
- Built-in signal/slot support
218-
- Async task queue
219-
- Graceful initialization/shutdown
220-
- Thread-safe communication
184+
TSignal is licensed under the MIT License. See [LICENSE](LICENSE) for details.

0 commit comments

Comments
 (0)