Skip to content

Commit 8d9e703

Browse files
authored
Merge pull request #5 from TSignalDev/weakref
feat: Add weak reference and one-shot connection support
2 parents 0ae7cfd + fbc3c6a commit 8d9e703

31 files changed

+1590
-165
lines changed

Diff for: CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.4.0] - 2024-12-21
8+
9+
### Added
10+
- **Weak Reference Support**: Introduced `weak=True` for signal connections to allow automatic disconnection when the receiver is garbage-collected.
11+
- **One-Shot Connections**: Added `one_shot=True` in `connect(...)` to enable automatically disconnecting a slot after its first successful emission call.
12+
- Extended integration tests to cover new `weak` and `one_shot` functionality.
13+
14+
### Improved
15+
- **Thread Safety**: Strengthened internal locking and concurrency patterns to reduce race conditions in high-load or multi-threaded environments.
16+
- **Documentation**: Updated `readme.md`, `api.md`, and example code sections to explain weak references, one-shot usage, and improved thread-safety details.
17+
718
## [0.3.0] - 2024-12-19
819

920
### Changed

Diff for: README.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ TSignal is a lightweight, pure-Python signal/slot library that provides thread-s
1010
- **Flexible Connection Types**: Direct or queued connections, automatically chosen based on the caller and callee threads.
1111
- **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.
1212
- **Familiar Decorators**: Inspired by Qt’s pattern, `@t_with_signals`, `@t_signal`, and `@t_slot` let you define signals and slots declaratively.
13+
- **Weak Reference**:
14+
- By setting `weak=True` when connecting a slot, the library holds a weak reference to the receiver object. This allows the receiver to be garbage-collected if there are no other strong references to it. Once garbage-collected, the connection is automatically removed, preventing stale references.
1315

1416
## Why TSignal?
1517

@@ -100,6 +102,7 @@ asyncio.run(main())
100102
### Thread Safety and Connection Types
101103
TSignal automatically detects whether the signal emission and slot execution occur in the same thread or different threads:
102104

105+
- **Auto Connection**: When connection_type is AUTO_CONNECTION (default), TSignal checks whether the slot is a coroutine function or whether the caller and callee share the same thread affinity. If they are the same thread and slot is synchronous, it uses direct connection. Otherwise, it uses queued connection.
103106
- **Direct Connection**: If signal and slot share the same thread affinity, the slot is invoked directly.
104107
- **Queued Connection**: If they differ, the call is queued to the slot’s thread/event loop, ensuring thread safety.
105108

Diff for: docs/api.md

+27-5
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ Represents a signal. Signals are created by `@t_signal` and accessed as class at
126126
Connects the signal to a slot.
127127

128128
- **Parameters:**
129-
- receiver_or_slot: Either the receiver object and slot method, or just a callable (function/lambda) if slot is None.
130-
- slot: The method in the receiver if a receiver object is provided.
131-
- connection_type: DIRECT_CONNECTION, QUEUED_CONNECTION, or AUTO_CONNECTION.
132-
- AUTO_CONNECTION (default): Determines connection type automatically based on thread affinity and slot type.
129+
- **receiver_or_slot:** Either the receiver object and slot method, or just a callable (function/lambda) if slot is None.
130+
- **slot:** The method in the receiver if a receiver object is provided.
131+
- **connection_type:** DIRECT_CONNECTION, QUEUED_CONNECTION, or AUTO_CONNECTION.
132+
- **AUTO_CONNECTION (default):** Determines connection type automatically based on thread affinity and slot type.
133+
- **weak:** If `True`, the receiver is kept via a weak reference so it can be garbage collected once there are no strong references. The signal automatically removes the connection if the receiver is collected.
134+
- **one_shot:** If `True`, the connection is automatically disconnected after the first successful emit call. This is useful for events that should only notify a slot once.
133135

134136
**Examples:**
135137

@@ -151,9 +153,29 @@ signal.connect(print)
151153

152154
Disconnects a previously connected slot. Returns the number of disconnected connections.
153155

156+
- **Parameters:**
157+
- receiver: The object whose slot is connected. If receiver is None, all receivers are considered.
158+
- slot: The specific slot to disconnect from the signal. If slot is None, all slots for the given receiver (or all connections if receiver is also None) are disconnected.
159+
- **Returns:** The number of connections that were disconnected.-
160+
161+
**Examples:**
162+
```python
163+
# Disconnect all connections
164+
signal.disconnect()
165+
166+
# Disconnect all slots from a specific receiver
167+
signal.disconnect(receiver=my_receiver)
168+
169+
# Disconnect a specific slot from a specific receiver
170+
signal.disconnect(receiver=my_receiver, slot=my_receiver.some_slot)
171+
172+
# Disconnect a standalone function
173+
signal.disconnect(slot=my_function)
174+
```
175+
154176
`emit(*args, **kwargs) -> None`
155177

156-
Emits the signal, invoking all connected slots either directly or via the event loop of the slot’s associated thread.
178+
Emits the signal, invoking all connected slots either directly or via the event loop of the slot’s associated thread, depending on the connection type. If a connection is marked one_shot, it is automatically removed right after invocation.
157179

158180
`TConnectionType`
159181

Diff for: docs/testing.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ tests/
1313
│ ├── test_property.py
1414
│ ├── test_signal.py
1515
│ ├── test_slot.py
16-
│ └── test_utils.py
16+
│ ├── test_utils.py
17+
│ └── test_weak.py
1718
├── integration/ # Integration tests
1819
│ ├── __init__.py
1920
│ ├── test_async.py

0 commit comments

Comments
 (0)