Metadata-Version: 2.1
Name: psygnal
Version: 0.8.1
Summary: Fast python callback/event system modeled after Qt Signals
Project-URL: homepage, https://github.com/pyapp-kit/psygnal
Project-URL: repository, https://github.com/pyapp-kit/psygnal
Project-URL: documentation, https://psygnal.readthedocs.io
Author-email: Talley Lambert <talley.lambert@gmail.com>
License: BSD 3-Clause License
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: BSD License
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Typing :: Typed
Requires-Python: >=3.7
Requires-Dist: importlib-metadata; python_version < '3.8'
Requires-Dist: mypy-extensions
Requires-Dist: typing-extensions
Provides-Extra: dev
Requires-Dist: black; extra == 'dev'
Requires-Dist: cruft; extra == 'dev'
Requires-Dist: dask; extra == 'dev'
Requires-Dist: ipython; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: numpy; extra == 'dev'
Requires-Dist: pre-commit; extra == 'dev'
Requires-Dist: pydantic; extra == 'dev'
Requires-Dist: pyqt5; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest-qt; extra == 'dev'
Requires-Dist: qtpy; extra == 'dev'
Requires-Dist: rich; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: wrapt; extra == 'dev'
Provides-Extra: docs
Requires-Dist: griffe==0.25.5; extra == 'docs'
Requires-Dist: mkdocs-material==8.5.10; extra == 'docs'
Requires-Dist: mkdocs-minify-plugin; extra == 'docs'
Requires-Dist: mkdocs-spellcheck[all]; extra == 'docs'
Requires-Dist: mkdocs==1.4.2; extra == 'docs'
Requires-Dist: mkdocstrings-python==0.8.3; extra == 'docs'
Requires-Dist: mkdocstrings==0.20.0; extra == 'docs'
Provides-Extra: proxy
Requires-Dist: wrapt; extra == 'proxy'
Provides-Extra: pydantic
Requires-Dist: pydantic; extra == 'pydantic'
Provides-Extra: test
Requires-Dist: dask; extra == 'test'
Requires-Dist: msgspec; python_version >= '3.8' and extra == 'test'
Requires-Dist: numpy; extra == 'test'
Requires-Dist: pydantic; extra == 'test'
Requires-Dist: pytest-codspeed; extra == 'test'
Requires-Dist: pytest-cov; extra == 'test'
Requires-Dist: pytest>=6.0; extra == 'test'
Requires-Dist: wrapt; extra == 'test'
Provides-Extra: testqt
Requires-Dist: pytest-qt; extra == 'testqt'
Requires-Dist: qtpy; extra == 'testqt'
Description-Content-Type: text/markdown

# psygnal

[![License](https://img.shields.io/pypi/l/psygnal.svg?color=green)](https://github.com/pyapp-kit/psygnal/raw/master/LICENSE)
[![PyPI](https://img.shields.io/pypi/v/psygnal.svg?color=green)](https://pypi.org/project/psygnal)
![Conda](https://img.shields.io/conda/v/conda-forge/psygnal)
[![Python Version](https://img.shields.io/pypi/pyversions/psygnal.svg?color=green)](https://python.org)
[![CI](https://github.com/pyapp-kit/psygnal/actions/workflows/test.yml/badge.svg)](https://github.com/pyapp-kit/psygnal/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/pyapp-kit/psygnal/branch/main/graph/badge.svg?token=qGnz9GXpEb)](https://codecov.io/gh/pyapp-kit/psygnal)
[![Documentation Status](https://readthedocs.org/projects/psygnal/badge/?version=latest)](https://psygnal.readthedocs.io/en/latest/?badge=latest)
[![Benchmarks](https://img.shields.io/badge/⏱-codspeed-%23FF7B53)](https://codspeed.io/pyapp-kit/psygnal)

Psygnal (pronounced "signal") is a pure python implementation of the [observer
pattern](https://en.wikipedia.org/wiki/Observer_pattern), with the API of
[Qt-style Signals](https://doc.qt.io/qt-5/signalsandslots.html) with (optional)
signature and type checking, and support for threading.

> This library does ***not*** require or use Qt in any way, It simply implements
> a similar observer pattern API.

## Documentation

https://psygnal.readthedocs.io/

### Install

```sh
pip install psygnal
```

```sh
conda install -c conda-forge psygnal
```

## Usage

The [observer pattern](https://en.wikipedia.org/wiki/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:

```python
from psygnal import Signal

class MyObject:
    # define one or 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](https://psygnal.readthedocs.io/)!

### Evented Dataclasses

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](https://docs.python.org/3/library/dataclasses.html),
as well as [attrs](https://www.attrs.org/en/stable/), and
[pydantic](https://pydantic-docs.helpmanual.io):

```python
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](https://psygnal.readthedocs.io/en/latest/dataclasses/) for more details.


## Benchmark history

https://pyapp-kit.github.io/psygnal/

and

https://codspeed.io/pyapp-kit/psygnal

## Developers

### Debugging

While `psygnal` is a pure python module, it is compiled with mypyc to increase
performance.  To disable all compiled files and run the pure python version,
you may run:

```bash
python -c "import psygnal.utils; psygnal.utils.decompile()"
```

To return the compiled version, run:

```bash
python -c "import psygnal.utils; psygnal.utils.recompile()"
```

The `psygnal._compiled` variable will tell you if you're using the compiled
version or not.
