Psygnal (pronounced "signal") is a pure python implementation of the observer pattern, with the API of Qt-style Signals with (optional) signature and type checking, and support for threading. It has no dependencies.
This library does not require or use Qt in any way, It simply implements a similar observer pattern API.
https://psygnal.readthedocs.io/
conda install -c conda-forge psygnal
The observer pattern is a software design pattern in which an object maintains a list of its dependents ("observers"), and notifies them of any state changes – usually by calling a callback function provided by the observer.
Here is a simple example of using psygnal:
from psygnal import Signal class MyObject: # define one or more signals as class attributes value_changed = Signal(str) # create an instance my_obj = MyObject() # You (or others) can connect callbacks to your signals @my_obj.value_changed.connect def on_change(new_value: str): print(f"The value changed to {new_value}!") # The object may now emit signals when appropriate, # (for example in a setter method) my_obj.value_changed.emit('hi') # prints "The value changed to hi!"
Much more detail available in the documentation!
A particularly nice usage of the signal pattern is to emit signals whenever a field of a dataclass changes. Psygnal provides an @evented
decorator that will emit a signal whenever a field changes. It is compatible with dataclasses
from the standard library, as well as attrs, and pydantic:
from psygnal import evented from dataclasses import dataclass @evented @dataclass class Person: name: str age: int = 0 person = Person('John', age=30) # connect callbacks @person.events.age.connect def _on_age_change(new_age: str): print(f"Age changed to {new_age}") person.age = 31 # prints: Age changed to 31
See the dataclass documentation for more details.
psygnal.containers
provides evented versions of mutable data structures (dict
, list
, set
), for cases when you need to monitor mutation:
from psygnal.containers import EventedList my_list = EventedList([1, 2, 3, 4, 5]) my_list.events.inserted.connect(lambda i, val: print(f"Inserted {val} at index {i}")) my_list.events.removed.connect(lambda i, val: print(f"Removed {val} at index {i}")) my_list.append(6) # Output: Inserted 6 at index 5 my_list.pop() # Output: Removed 6 at index 5
See the evented containers documentation for more details.
https://pyapp-kit.github.io/psygnal/
and
https://codspeed.io/pyapp-kit/psygnal
This project uses PEP 735 dependency groups.
After cloning, setup your env with uv sync
or pip install -e . --group dev
While psygnal
is a pure python package, it is compiled with mypyc to increase performance. To test the compiled version locally, you can run:
HATCH_BUILD_HOOKS_ENABLE=1 uv sync --force-reinstall
(which is also available as make build
if you have make installed)
To disable all compiled files and run the pure python version, you may run:
python -c "import psygnal.utils; psygnal.utils.decompile()"
To return the compiled version, run:
python -c "import psygnal.utils; psygnal.utils.recompile()"
The psygnal._compiled
variable will tell you if you're using the compiled version or not.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4