Skip to content

feat: Add worker thread pattern and enhance signal connections #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/tests.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
name: Tests
# .github/workflows/ci.yml
name: CI

on:
push:
Expand All @@ -11,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
python-version: ['3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v2
Expand All @@ -24,9 +25,18 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pytest pytest-cov pytest-asyncio
pip install -e ".[dev]"
pip install pylint mypy

- name: Run tests
run: |
pytest --cov=tsignal

- name: Code quality checks
run: |
pylint src/tsignal
mypy src/tsignal

- name: Performance tests
run: |
pytest -v -m performance
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,54 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] - 2024-03-19

### Changed
- Updated minimum Python version requirement to 3.10
- This change was necessary to ensure reliable worker thread functionality
- Python 3.10+ provides improved async features and type handling
- Better support for async context management and error handling
- Updated documentation to reflect new Python version requirement
- Enhanced worker thread implementation with Python 3.10+ features

### Added
- Performance tests for stress testing and memory usage analysis
- Includes `test_stress.py` for heavy signal load testing
- Includes `test_memory.py` for memory profiling

### Removed
- Support for Python versions below 3.10

### Note
Core features are now implemented and stable:
- Robust signal-slot mechanism
- Thread-safe operations
- Async/await support
- Worker thread pattern
- Comprehensive documentation
- Full test coverage

Next steps before 1.0.0:
- Additional stress testing
- Memory leak verification
- Production environment validation
- Enhanced CI/CD pipeline
- Extended documentation

## [0.1.1] - 2024-12-01

### Changed
- Refactored signal connection logic to support direct function connections
- Improved error handling for invalid connections
- Enhanced logging for signal emissions and connections

### Fixed
- Resolved issues with disconnecting slots during signal emissions
- Fixed bugs related to async slot processing and connection management

### Removed
- Deprecated unused constants and methods from the core module

## [0.1.0] - 2024-01-26

### Added
Expand Down
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ asyncio.run(main())
```

## Features
- Requires Python 3.10+
- Easy-to-use signal-slot mechanism with decorators
- Support for both synchronous and asynchronous slots
- Thread-safe signal emissions
Expand All @@ -78,7 +79,7 @@ asyncio.run(main())

## Installation

Currently, this package is under development. You can install it directly from the repository:
TSignal requires Python 3.10 or higher. You can install it directly from the repository:

```bash
git clone https://github.com/tsignal/tsignal-python.git
Expand Down Expand Up @@ -132,3 +133,88 @@ Please see [Contributing Guidelines](CONTRIBUTING.md) for details on how to cont

## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Connecting Signals and Slots

### Classic Object-Member Connection
```python
@t_with_signals
class Counter:
@t_signal
def count_changed(self):
pass

@t_with_signals
class Display:
@t_slot
def on_count_changed(self, value):
print(f"Count is now: {value}")

counter = Counter()
display = Display()
counter.count_changed.connect(display, display.on_count_changed)
```

### Function Connection
```python
# Connect to a simple function
def print_value(value):
print(f"Value: {value}")

counter.count_changed.connect(print_value)

# Connect to a lambda
counter.count_changed.connect(lambda x: print(f"Lambda received: {x}"))

# Connect to an object method without @t_slot
class Handler:
def process_value(self, value):
print(f"Processing: {value}")

handler = Handler()
counter.count_changed.connect(handler.process_value)
```

## Worker Thread Pattern

TSignal provides a worker thread pattern that combines thread management with signal/slot communication and task queuing:

```python
from tsignal import t_with_worker

@t_with_worker
class DataProcessor:
async def initialize(self, config=None):
# Setup worker (called in worker thread)
self.config = config or {}

async def process_data(self, data):
# Heavy processing in worker thread
result = await heavy_computation(data)
self.processing_done.emit(result)

async def finalize(self):
# Cleanup worker (called before thread stops)
await self.cleanup()

@t_signal
def processing_done(self):
pass

# Usage
processor = DataProcessor()
processor.start(config={'threads': 4}) # Starts worker thread

# Queue task in worker thread
await processor.queue_task(processor.process_data(some_data))

# Stop worker
processor.stop() # Graceful shutdown
```

The worker pattern provides:
- Dedicated worker thread with event loop
- Built-in signal/slot support
- Async task queue
- Graceful initialization/shutdown
- Thread-safe communication
94 changes: 76 additions & 18 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,88 @@ async def on_async_signal(self, *args, **kwargs):
pass
```

### `@t_with_worker`
Class decorator that creates a worker thread with signal support and task queue.

**Requirements:**
- Class must implement async `initialize(self, *args, **kwargs)` method
- Class must implement async `finalize(self)` method

**Added Methods:**
##### `start(*args, **kwargs) -> None`
Starts the worker thread and calls initialize with given arguments.

##### `stop() -> None`
Stops the worker thread gracefully, calling finalize.

##### `async queue_task(coro) -> None`
Queues a coroutine for execution in the worker thread.

**Example:**
```python
@t_with_worker
class Worker:
async def initialize(self):
print("Worker initialized")

async def finalize(self):
print("Worker cleanup")

async def process(self):
await asyncio.sleep(1)
print("Processing done")

worker = Worker()
worker.start()
await worker.queue_task(worker.process())
worker.stop()
```

## Classes

### `TSignal`
Base class for signals.

#### Methods

##### `connect(receiver: object, slot: Callable, connection_type: Optional[TConnectionType] = None) -> None`
##### `connect(receiver_or_slot: Union[object, Callable], slot: Optional[Callable] = None) -> None`
Connects the signal to a slot.

**Parameters:**
- `receiver`: Object that contains the slot
- `slot`: Callable that will receive the signal
- `connection_type`: Optional connection type (DirectConnection or QueuedConnection)
- When connecting to a QObject slot:
- `receiver_or_slot`: The receiver object
- `slot`: The slot method of the receiver

- When connecting to a function/lambda:
- `receiver_or_slot`: The callable (function, lambda, or method)
- `slot`: None

**Connection Behavior:**
1. Object Method with Signal Support:
```python
@t_with_signals
class Receiver:
def on_signal(self, value):
print(value)

receiver = Receiver()
signal.connect(receiver.on_signal) # Automatically sets up receiver
```

2. Regular Object Method:
```python
class RegularClass:
def on_signal(self, value):
print(value)

obj = RegularClass()
signal.connect(obj.on_signal) # Treated as direct connection
```

The connection type is automatically determined:
- Methods from objects with `@t_with_signals` are set up with their object as receiver
- Regular object methods are treated as direct connections
- Async methods always use queued connections

##### `disconnect(receiver: Optional[object] = None, slot: Optional[Callable] = None) -> int`
Disconnects one or more slots from the signal.
Expand Down Expand Up @@ -104,18 +172,8 @@ Emits the signal with the given arguments.
Enum defining connection types.

#### Values:
- `DirectConnection`: Slot is called directly in the emitting thread
- `QueuedConnection`: Slot is queued in the receiver's event loop

## Constants

### `TSignalConstants`
Constants used by the TSignal system.

#### Values:
- `FROM_EMIT`: Key for emission context
- `THREAD`: Key for thread storage
- `LOOP`: Key for event loop storage
- `DIRECT_CONNECTION`: Slot is called directly in the emitting thread
- `QUEUED_CONNECTION`: Slot is queued in the receiver's event loop

## Usage Examples

Expand Down Expand Up @@ -164,14 +222,14 @@ asyncio.run(main())
sender.value_changed.connect(
receiver,
receiver.on_value_changed,
connection_type=TConnectionType.DirectConnection
connection_type=TConnectionType.DIRECT_CONNECTION
)

# Force queued connection
sender.value_changed.connect(
receiver,
receiver.on_value_changed,
connection_type=TConnectionType.QueuedConnection
connection_type=TConnectionType.QUEUED_CONNECTION
)
```

Expand Down
3 changes: 3 additions & 0 deletions docs/logging.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Logging Guidelines

## Requirements
TSignal requires Python 3.10 or higher.

TSignal uses Python's standard logging module with the following levels:

- DEBUG: Detailed information about signal-slot connections and emissions
Expand Down
Loading
Loading