PynneX is a lightweight, pure-Python emitter-listener (signal-slot) library that provides thread-safe, asyncio-compatible event handling. It enables clean decoupling of components, seamless thread-to-thread communication, and flexible asynchronous/synchronous listener handling. If you prefer Qt-style naming, signal-slot aliases are also available, as well as publisher-subscriber for a Pub/Sub feel.
asyncio
.@nx_with_emitters
, @nx_emitter
, @nx_listener
, @nx_with_worker
nx_
prefix@with_signals
, @signal
, @slot
@with_publishers
, @publisher
, @subscriber
@nx_property
decorator offers property access with automatic emitter notification on changes.weak=True
when connecting a listener, the library holds a weak reference to the receiver object. If no other strong reference exists, garbage collection removes both the object and its connection, avoiding stale references.Modern Python applications often combine asynchronous operations and multithreading. Many event-driven frameworks come with large dependencies or limited async/thread support. PynneX aims to fill the gap by offering:
asyncio
compatibility.Async-Ready
asyncio
workflowsThread-Safe by Design
Flexible Listeners
Robust Testing & Examples
PynneX requires Python 3.10+ for stable asyncio
operations:
git clone https://github.com/nexconnectio/pynnex.git
cd pynnex
pip install -e .
For development (includes tests and linting tools):
pip install -e ".[dev]"
from pynnex import with_emitters, emitter, listener
@with_emitters
class Counter:
def __init__(self):
self.count = 0
@emitter
def count_changed(self):
pass
def increment(self):
self.count += 1
self.count_changed.emit(self.count)
@with_emitters
class Display:
@listener
async def on_count_changed(self, value):
print(f"Count is now: {value}")
# Connect and use
counter = Counter()
display = Display()
counter.count_changed.connect(display, display.on_count_changed)
counter.increment() # Will print: "Count is now: 1"
@with_emitters
class AsyncDisplay:
@listener
async def on_count_changed(self, value):
await asyncio.sleep(1) # Simulate async operation
print(f"Count updated to: {value}")
# Usage in async context
async def main():
counter = Counter()
display = AsyncDisplay()
counter.count_changed.connect(display, display.on_count_changed)
counter.increment()
# Wait for async processing
await asyncio.sleep(1.1)
asyncio.run(main())
@emitter
. They’re callable attributes in a class that can emit events to notify interested listeners.@listener
. They’re methods that respond to an emitter’s event. Both sync and async are supported.emitter.connect(receiver, listener)
to wire them up. You can also connect standalone functions or lambdas.PynneX detects whether the emitter call and listener execution happen in the same thread or different ones:
For background work, PynneX provides a @nx_with_worker
(alias @with_worker
) decorator that:
** Worker Example **
from pynnex import with_worker, emitter
@with_worker
class DataProcessor:
@emitter
def processing_done(self):
pass
async def run(self, *args, **kwargs):
# The main entry point for the worker thread’s event loop
# Wait for tasks or stopping signal
await self.wait_for_stop()
async def process_data(self, data):
# Perform heavy computation in the worker thread
result = await heavy_computation(data)
self.processing_done.emit(result)
processor = DataProcessor()
processor.start()
# Queue a task to run in the worker thread:
processor.queue_task(processor.process_data(some_data))
# Stop the worker gracefully
processor.stop()
We’ve expanded PynneX’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.
Stock Monitor Console: Real-time price updates, alert configuration, and notification history in action
Stock Monitor UI: Real-time price updates, alert configuration, and notification history in action
pip install pynnex
(Python 3.10+)PynneX is licensed under the MIT License. See LICENSE for details.