{"id":28425053,"url":"https://github.com/fisothemes/pyesys","last_synced_at":"2025-06-28T04:31:11.451Z","repository":{"id":297377877,"uuid":"995834797","full_name":"fisothemes/pyesys","owner":"fisothemes","description":"An easy-to-use, robust, thread-safe, and type-safe event system for Python with comprehensive async support.","archived":false,"fork":false,"pushed_at":"2025-06-05T06:15:13.000Z","size":36,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-05T07:50:49.659Z","etag":null,"topics":["async","asyncio","decorators","event","eventsystem","pubsub","python","python3","pythoneventsystem","weakref"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fisothemes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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}},"created_at":"2025-06-04T04:39:32.000Z","updated_at":"2025-06-05T06:15:15.000Z","dependencies_parsed_at":"2025-06-06T18:33:18.027Z","dependency_job_id":null,"html_url":"https://github.com/fisothemes/pyesys","commit_stats":null,"previous_names":["fisothemes/pyesys"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/fisothemes/pyesys","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fisothemes%2Fpyesys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fisothemes%2Fpyesys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fisothemes%2Fpyesys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fisothemes%2Fpyesys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fisothemes","download_url":"https://codeload.github.com/fisothemes/pyesys/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fisothemes%2Fpyesys/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262375580,"owners_count":23301309,"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","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":["async","asyncio","decorators","event","eventsystem","pubsub","python","python3","pythoneventsystem","weakref"],"created_at":"2025-06-05T10:31:07.931Z","updated_at":"2025-06-28T04:31:11.443Z","avatar_url":"https://github.com/fisothemes.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Python Event System (PyESys)\n\n[![Test](https://github.com/fisothemes/pyesys/actions/workflows/test.yaml/badge.svg)](https://github.com/fisothemes/pyesys/actions/workflows/test.yaml)\n[![Python version](https://img.shields.io/badge/python-3.12%2B-blue.svg)](https://www.python.org/downloads/)\n[![PyPI version](https://img.shields.io/pypi/v/pyesys.svg)](https://pypi.org/project/pyesys/)\n[![Downloads](https://img.shields.io/pypi/dd/pyesys)](https://pypistats.org/packages/pyesys)\n[![Licence](https://img.shields.io/badge/licence-MIT-blue.svg)](LICENSE)\n\n**A Python-native event system with thread-safe, type-safe event handling and first-class async support.**\n\nPyESys brings clean, per-instance event handling to Python using familiar patterns like property descriptors and operator overloading. Perfect for real-time systems, simulations, and any application requiring robust event-driven architecture.\n\n```python\nfrom pyesys import event\n\nclass Button:\n    @event\n    def on_click(self):\n        \"\"\"Click event signature\"\"\"\n    \n    @on_click.emitter\n    def click(self):\n        print(\"Button clicked!\")\n\n# Each instance gets its own events\nbtn = Button()\nbtn.on_click += lambda: print(\"Handler executed!\")\nbtn.click()\n# Output: Button clicked!\n#         Handler executed!\n```\n\n## Why PyESys?\n\nWhile Python has several event handling solutions, many common approaches present challenges for modern applications:\n\n**Event Bus Patterns**: Many libraries use global event buses with string-based keys, which can create tight coupling and make per-instance event management complex. When each object instance needs its own events, you often need to manage ID strings or implement filtering logic.\n\n**Memory Management**: Event systems with bound methods can suffer from memory leaks if not carefully designed with weak references and proper cleanup mechanisms.\n\n**Async/Sync Integration**: Mixing synchronous and asynchronous event handlers consistently can be challenging, especially when you need both to coexist seamlessly.\n\nPyESys addresses these architectural challenges with a clean, intuitive API:\n\n- **Per-Instance Events**: No global registries or string-based keys. Each object manages its own events independently.\n- **Type Safety**: Runtime signature validation catches handler mismatches early.\n- **Async-Ready**: Mix sync and async handlers seamlessly with automatic thread pool handling.\n- **Pythonic**: Familiar `@event` decorator syntax inspired by `@property`, plus `+=`/`-=` operators.\n- **Memory Safe**: Built-in weak references prevent common memory leak patterns.\n- **Thread Safe**: Safe concurrent event emission across multiple threads.\n\n## Quick Start\n\n### Installation\n\n```bash\npip install pyesys\n```\n\nRequires Python 3.12+. Zero dependencies.\n\n### Basic Usage\n\n```python\nfrom pyesys import create_event\n\n# Create event with signature validation\nevent, listener = create_event(example=lambda msg: None)\n\ndef log_message(msg: str):\n    print(f\"[LOG] {msg}\")\n\n# Subscribe and emit\nlistener += log_message\nevent.emit(\"Hello PyESys!\")\n# Output: [LOG] Hello PyESys!\n```\n\n### Class-Based Events\n\n```python\nfrom pyesys import event\n\nclass FileProcessor:\n    @event\n    def on_progress(self, filename: str, percent: float):\n        \"\"\"Progress update event\"\"\"\n    \n    @on_progress.emitter\n    def _update_progress(self, filename: str, percent: float):\n        pass  # Event automatically emitted\n    \n    def process(self, filename: str):\n        for i in range(0, 101, 25):\n            self._update_progress(filename, i)\n\n# Each processor has independent events\nprocessor = FileProcessor()\nprocessor.on_progress += lambda f, p: print(f\"{f}: {p}% complete\")\n\nprocessor.process(\"data.txt\")\n# Output: data.txt: 0% complete\n#         data.txt: 25% complete\n#         ...\n```\n\n## Advanced Features\n\n### Event Chaining\n\nCreate processing pipelines by chaining events between objects:\n\n```python\nclass DataProcessor:\n    @event\n    def on_processed(self, data: dict):\n        pass\n    \n    @on_processed.emitter\n    def process(self, data: dict):\n        # Transform data\n        return {\"processed\": True, **data}\n\nclass DataValidator:\n    def validate(self, data: dict):\n        print(f\"Validating: {data}\")\n\nprocessor = DataProcessor()\nvalidator = DataValidator()\n\n# Chain processors\nprocessor.on_processed += validator.validate\nprocessor.process({\"id\": 123})\n```\n\n### Async Support\n\nMix synchronous and asynchronous handlers effortlessly:\n\n```python\nimport asyncio\n\nasync def async_handler(data):\n    await asyncio.sleep(0.1)\n    print(f\"Async: {data}\")\n\ndef sync_handler(data):\n    print(f\"Sync: {data}\")\n\nlistener += [sync_handler, async_handler]\nawait event.emit_async(\"mixed-handlers\")\n# Both handlers run concurrently\n```\n\n### Bulk Operations\n\nEfficiently manage multiple handlers:\n\n```python\n# Bulk subscribe\nlistener += [handler1, handler2, handler3]\n\n# Bulk unsubscribe  \nlistener -= {handler1, handler2}\n\n# Introspection\nprint(f\"Active handlers: {listener.handler_count()}\")\n```\n\n### Production Error Handling\n\n```python\ndef error_handler(exception, handler_func):\n    logger.error(f\"Handler {handler_func.__name__} failed: {exception}\")\n\nevent, listener = create_event(\n    example=lambda x: None,\n    error_handler=error_handler\n)\n# Failing handlers won't crash the system\n```\n\n## Real-World Use Cases\n\n- **Real-time Systems**: React to sensor inputs and control signals\n- **Simulation Frameworks**: Decouple models from visualization/control  \n- **Plugin Architectures**: Extend applications safely with event hooks\n- **UI/Backend Integration**: Bridge sync and async worlds seamlessly\n- **Testable Systems**: Replace complex callbacks with observable events\n\n## API Overview\n\n### Core Functions\n\n#### `create_event(example, *, allow_duplicates=True, error_handler=None)`\n\nCreates an event emitter and listener pair with signature validation:\n\n```python\nfrom pyesys import create_event\n\nevent, listener = create_event(\n    example=lambda x, y: None,  # Handler signature template\n    allow_duplicates=False,     # Prevent duplicate subscriptions\n    error_handler=custom_handler  # Handle exceptions gracefully\n)\n```\n\n#### `@event` Decorator\n\nCreates class-level or module-level events using familiar decorator syntax:\n\n```python\nfrom pyesys import event\n\n# Class-level event (per-instance)\nclass MyClass:\n    @event\n    def on_something(self, value: int):\n        \"\"\"Event signature: handlers must accept a single int.\"\"\"\n        pass\n\n    @on_something.emitter\n    def do_something(self, value: int):\n        \"\"\"This method triggers the on_something event after running.\"\"\"\n        print(f\"Doing something with {value}\")\n\n# Module-level event (global)\n@event\ndef on_global_event(message: str):\n    \"\"\"Global event signature: handlers must accept a single str.\"\"\"\n    pass\n\n@on_global_event.emitter\ndef trigger_global(message: str):\n    \"\"\"This function triggers the module-level event after running.\"\"\"\n    print(f\"Global: {message}\")\n```\n\n### Event Class\n\nThe `Event` class provides the core event management functionality:\n\n**Subscription Management:**\n- `event += handler` - Subscribe directly to event\n- `event -= handler` - Unsubscribe directly from event\n- `listener += handler` - Subscribe via listener interface\n- `listener -= handler` - Unsubscribe via listener interface\n- `event += [h1, h2, h3]` - Bulk subscribe\n- `event.clear()` - Remove all handlers\n\n**Event Emission:**\n- `event.emit(*args, **kwargs)` - Synchronous emission\n- `await event.emit_async(*args, **kwargs)` - Asynchronous emission\n\n**Introspection:**\n- `event.handler_count()` - Number of active handlers\n- `event.handlers` - List of current handlers\n- `bool(event)` - True if handlers exist\n- `len(event)` - Same as handler_count()\n\n### Key Features\n\n**Type Safety**: Runtime signature validation through example functions ensures handlers match expected signatures, catching errors early.\n\n**Memory Management**: Automatic weak reference handling for bound methods prevents memory leaks without requiring manual cleanup.\n\n**Thread Safety**: All operations are thread-safe, allowing safe concurrent access from multiple threads.\n\n**Async Integration**: Seamlessly mix sync and async handlers. Async handlers run concurrently, sync handlers run in thread pools during async emission.\n\n**Error Resilience**: Custom error handlers prevent one failing handler from affecting others, crucial for production systems.\n\n## Alternative Usage Patterns\n\n### Direct Event Instantiation\n\nFor more control, instantiate events directly:\n\n```python\nfrom pyesys.event import Event\n\ndef example_sig(a: int, b: str) -\u003e None:\n    pass\n\nmsg_event = Event(example=example_sig)\n\ndef on_message(a: int, b: str) -\u003e None:\n    print(f\"Got {a} and {b}\")\n\n# Subscribe directly to the event\nmsg_event += on_message\nmsg_event.emit(1, \"hello\")\n\n# Or use the listener interface\nmsg_event.listener += on_message\n```\n\n### Module-Level Events\n\nCreate application-wide events at module level:\n\n```python\nfrom pyesys import event\n\n@event\ndef on_user_login(user_id: str, timestamp: float) -\u003e None:\n    \"\"\"Fired when a user successfully logs in\"\"\"\n\n@on_user_login.emitter\ndef login_user(user_id: str, timestamp: float) -\u003e None:\n    \"\"\"Authenticate user and emit login event\"\"\"\n    print(f\"User {user_id} authenticated at {timestamp}\")\n\n# Subscribe to login events\ndef update_last_seen(user_id: str, timestamp: float):\n    print(f\"Updating last seen for {user_id}\")\n\non_user_login += update_last_seen\n```\n\n## Testing\n\nPyESys uses **pytest** and **pytest-asyncio**. To install dev dependencies and run the test suite:\n\n```bash\npip install -e .[dev]\npytest -q\n```\n\nTest files live under `tests/`:\n\n- `tests/unit/test_handler.py` – tests for `EventHandler`\n- `tests/unit/test_event.py` – tests for `Event` (sync \u0026 async)\n- `tests/unit/test_prop.py` – tests for the `@event` decorator\n- `tests/integration/test_pyesys_end_to_end.py` – end-to-end integration tests\n\nAll tests must pass before merging any changes.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for details on:\n\n- Setting up a development environment\n- Branching and workflow conventions\n- Coding style \u0026 formatting (PEP 8, Black, type hints)\n- Writing tests and running them\n- Submitting pull requests\n\n## Documentation\n\n- **📖 [Full Documentation](https://fisothemes.github.io/pyesys/)**\n- **🐛 [Issue Tracker](https://github.com/fisothemes/pyesys/issues)**\n- **📋 [Examples](./examples/)**\n\n---\n\n*PyESys - Pythonic events for modern applications* 🐍✨","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffisothemes%2Fpyesys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffisothemes%2Fpyesys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffisothemes%2Fpyesys/lists"}