r/Python 7d ago

News [Release 0.4.0] TSignal: A Flexible Python Signal/Slot System for Async and Threaded Python—Now with

Hey everyone!

I’m thrilled to announce TSignal 0.4.0, a pure-Python signal/slot library that helps you build event-driven applications with ease. TSignal integrates smoothly with async/await, handles thread safety for you, and doesn’t force you to install heavy frameworks.

What’s New in 0.4.0

Weak Reference Support

You can now connect a slot with weak=True. If the receiver object is garbage-collected, TSignal automatically removes the connection, preventing memory leaks or stale slots in long-lived applications:

```python

Set weak=True for individual connections

sender.event.connect(receiver, receiver.on_event, weak=True)

Or, set weak_default=True at class level (default is True)

@t_with_signals(weak_default=True) class WeakRefSender: @t_signal def event(self): pass

Now all connections from this sender will use weak references by default

No need to specify weak=True for each connect call

sender = WeakRefSender() sender.event.connect(receiver, receiver.on_event) # Uses weak reference

Once receiver is GC’d, TSignal cleans up automatically.

```

One-Shot Connections (Optional)

A new connection parameter, one_shot=True, lets you disconnect a slot right after its first call. It’s handy for “listen-once” or “single handshake” scenarios. Just set:

python signal.connect(receiver, receiver.handler, one_shot=True)

The slot automatically goes away after the first emit.

Thread-Safety Improvements

TSignal’s internal locking and scheduling mechanisms have been refined to further reduce race conditions in high-concurrency environments. This ensures more robust behavior under demanding multi-thread loads.

From Basics to Practical Use Cases

We’ve expanded TSignal’s examples to guide you from simple demos to full-fledged applications. Each example has its own GitHub link with fully commented code.

For detailed explanations, code walkthroughs, and architecture diagrams of these examples, check out our Examples Documentation.

Basic Signal/Slot Examples

Multi-Threading and Workers

  • thread_basic.py and thread_worker.py
    • walk you through multi-threaded setups, including background tasks and worker loops.
    • You’ll see how signals emitted from a background thread are properly handled in the main event loop or another thread’s loop.

Stock Monitor (Console & GUI)

  • stock_monitor_simple.py

    • A minimal stock monitor that periodically updates a display. Perfect for learning how TSignal can orchestrate real-time updates without blocking.
  • stock_monitor_console.py

    • A CLI-based interface that lets you type commands to set alerts, list them, and watch stock data update in real time.
  • stock_monitor_ui.py

    • A more elaborate Kivy-based UI example showcasing real-time stock monitoring. You'll see how TSignal updates the interface instantly without freezing the GUI. This example underscores how TSignal’s thread and event-loop management keeps your UI responsive and your background tasks humming.

Together, these examples highlight TSignal’s versatility—covering everything from quick demos to production-like patterns with threads, queues, and reactive UI updates.

Why TSignal?

Pure Python, No Heavy Frameworks TSignal imposes no large dependencies; it’s a clean library you can drop into your existing code.

Async-Ready

Built for modern asyncio workflows; you can define async slots that are invoked without blocking your event loop.

Thread-Safe by Design

Signals are dispatched to the correct thread or event loop behind the scenes, so you don’t have to manage locks.

Flexible Slots

Connect to class methods, standalone functions, or lambdas. Use strong references (the usual approach) or weak=True.

Robust Testing & Examples

We’ve invested heavily in test coverage, plus we have real-world examples (including a GUI!) to showcase best practices.

Quick Example

```python from tsignal import t_with_signals, t_signal, t_slot

@twith_signals class Counter: def __init_(self): self.count = 0

@t_signal
def count_changed(self):
    pass

def increment(self):
    self.count += 1
    self.count_changed.emit(self.count)

@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) counter.increment()

Output: "Count is now: 1"

```

Get Started

  • GitHub Repo: TSignal on GitHub
  • Documentation & Examples: Explore how to define your own signals and slots, integrate with threads, or build a reactive UI.
  • Issues & PRs: We welcome feedback, bug reports, and contributions.

If you’re building async or threaded Python apps that could benefit from a robust event-driven approach, give TSignal a try. We’d love to know what you think—open an issue or share your experience!

Thanks for checking out TSignal 0.4.0, and happy coding!

29 Upvotes

0 comments sorted by