{"id":19921503,"url":"https://github.com/pyapp-kit/psygnal","last_synced_at":"2025-10-05T13:17:16.304Z","repository":{"id":36952839,"uuid":"383130468","full_name":"pyapp-kit/psygnal","owner":"pyapp-kit","description":"Python observer pattern (callback/event system).  Modeled after Qt Signals \u0026 Slots (but independent of Qt)","archived":false,"fork":false,"pushed_at":"2025-09-24T11:32:32.000Z","size":962,"stargazers_count":113,"open_issues_count":9,"forks_count":21,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-24T13:22:15.067Z","etag":null,"topics":["callbacks","python","signalslot"],"latest_commit_sha":null,"homepage":"https://psygnal.readthedocs.io/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyapp-kit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-07-05T12:16:30.000Z","updated_at":"2025-09-24T11:30:16.000Z","dependencies_parsed_at":"2024-02-01T00:28:31.756Z","dependency_job_id":"455ac300-5e1c-4bc5-8a3a-ab03e46aa56e","html_url":"https://github.com/pyapp-kit/psygnal","commit_stats":{"total_commits":354,"total_committers":11,"mean_commits":32.18181818181818,"dds":"0.33615819209039544","last_synced_commit":"6db5219542c9e2e5a7f2cdcc3bdc30d696a9e803"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"purl":"pkg:github/pyapp-kit/psygnal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyapp-kit%2Fpsygnal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyapp-kit%2Fpsygnal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyapp-kit%2Fpsygnal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyapp-kit%2Fpsygnal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyapp-kit","download_url":"https://codeload.github.com/pyapp-kit/psygnal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyapp-kit%2Fpsygnal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278459460,"owners_count":25990351,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["callbacks","python","signalslot"],"created_at":"2024-11-12T22:07:46.790Z","updated_at":"2025-10-05T13:17:16.285Z","avatar_url":"https://github.com/pyapp-kit.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# psygnal\n\n[![License](https://img.shields.io/pypi/l/psygnal.svg?color=green)](https://github.com/pyapp-kit/psygnal/raw/master/LICENSE)\n[![PyPI](https://img.shields.io/pypi/v/psygnal.svg?color=green)](https://pypi.org/project/psygnal)\n[![Conda](https://img.shields.io/conda/v/conda-forge/psygnal)](https://github.com/conda-forge/psygnal-feedstock)\n[![Python Version](https://img.shields.io/pypi/pyversions/psygnal.svg?color=green)](https://python.org)\n[![CI](https://github.com/pyapp-kit/psygnal/actions/workflows/test.yml/badge.svg)](https://github.com/pyapp-kit/psygnal/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/pyapp-kit/psygnal/branch/main/graph/badge.svg?token=qGnz9GXpEb)](https://codecov.io/gh/pyapp-kit/psygnal)\n[![Documentation Status](https://readthedocs.org/projects/psygnal/badge/?version=latest)](https://psygnal.readthedocs.io/en/latest/?badge=latest)\n[![Benchmarks](https://img.shields.io/badge/⏱-codspeed-%23FF7B53)](https://codspeed.io/pyapp-kit/psygnal)\n\nPsygnal (pronounced \"signal\") is a pure python implementation of the [observer\npattern](https://en.wikipedia.org/wiki/Observer_pattern), with the API of\n[Qt-style Signals](https://doc.qt.io/qt-5/signalsandslots.html) with (optional)\nsignature and type checking, and support for threading.  It has no dependencies.\n\n\u003e This library does ***not*** require or use Qt in any way, It simply implements\n\u003e a similar observer pattern API.\n\n## Documentation\n\nhttps://psygnal.readthedocs.io/\n\n### Install\n\n```sh\npip install psygnal\n```\n\n```sh\nconda install -c conda-forge psygnal\n```\n\n## Usage\n\nThe [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.\n\nHere is a simple example of using psygnal:\n\n```python\nfrom psygnal import Signal\n\nclass MyObject:\n    # define one or more signals as class attributes\n    value_changed = Signal(str)\n\n# create an instance\nmy_obj = MyObject()\n\n# You (or others) can connect callbacks to your signals\n@my_obj.value_changed.connect\ndef on_change(new_value: str):\n    print(f\"The value changed to {new_value}!\")\n\n# The object may now emit signals when appropriate,\n# (for example in a setter method)\nmy_obj.value_changed.emit('hi')  # prints \"The value changed to hi!\"\n```\n\nMuch more detail available in the [documentation](https://psygnal.readthedocs.io/)!\n\n### Evented Dataclasses\n\nA particularly nice usage of the signal pattern is to emit signals whenever a\nfield of a dataclass changes. Psygnal provides an `@evented` decorator that will\nemit a signal whenever a field changes.  It is compatible with `dataclasses`\nfrom [the standard library](https://docs.python.org/3/library/dataclasses.html),\nas well as [attrs](https://www.attrs.org/en/stable/), and\n[pydantic](https://pydantic-docs.helpmanual.io):\n\n```python\nfrom psygnal import evented\nfrom dataclasses import dataclass\n\n@evented\n@dataclass\nclass Person:\n    name: str\n    age: int = 0\n\nperson = Person('John', age=30)\n\n# connect callbacks\n@person.events.age.connect\ndef _on_age_change(new_age: str):\n    print(f\"Age changed to {new_age}\")\n\nperson.age = 31  # prints: Age changed to 31\n```\n\nSee the [dataclass documentation](https://psygnal.readthedocs.io/en/latest/dataclasses/) for more details.\n\n### Evented Containers\n\n`psygnal.containers` provides evented versions of mutable data structures\n(`dict`, `list`, `set`), for cases when you need to monitor mutation:\n\n```python\nfrom psygnal.containers import EventedList\n\nmy_list = EventedList([1, 2, 3, 4, 5])\n\nmy_list.events.inserted.connect(lambda i, val: print(f\"Inserted {val} at index {i}\"))\nmy_list.events.removed.connect(lambda i, val: print(f\"Removed {val} at index {i}\"))\n\nmy_list.append(6)  # Output: Inserted 6 at index 5\nmy_list.pop()  # Output: Removed 6 at index 5\n```\n\nSee the\n[evented containers documentation](https://psygnal.readthedocs.io/en/latest/API/containers/)\nfor more details.\n\n## Benchmark history\n\nhttps://pyapp-kit.github.io/psygnal/\n\nand\n\nhttps://codspeed.io/pyapp-kit/psygnal\n\n## Developers\n\n### Setup\n\nThis project uses PEP 735 dependency groups.\n\nAfter cloning, setup your env with `uv sync` or `pip install -e . --group dev`\n\n### Compiling\n\nWhile `psygnal` is a pure python package, it is compiled with mypyc to increase\nperformance.  To test the compiled version locally, you can run:\n\n```bash\nHATCH_BUILD_HOOKS_ENABLE=1 uv sync --force-reinstall\n```\n\n(which is also available as `make build` if you have make installed)\n\n### Debugging\n\n To disable all compiled files and run the pure python version,\nyou may run:\n\n```bash\npython -c \"import psygnal.utils; psygnal.utils.decompile()\"\n```\n\nTo return the compiled version, run:\n\n```bash\npython -c \"import psygnal.utils; psygnal.utils.recompile()\"\n```\n\nThe `psygnal._compiled` variable will tell you if you're using the compiled\nversion or not.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyapp-kit%2Fpsygnal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyapp-kit%2Fpsygnal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyapp-kit%2Fpsygnal/lists"}