Skip to main content

Strongly-typed event decorator

Project description

typed-event

Strongly-typed events for Python via an @event decorator.

typed-event lets you define an event from a function/method prototype, then subscribe listeners with += and unsubscribe with -=.

Installation

pip install typed-event

Quick start

Module-level event

from typed_event import event


@event
def counter_changed(new_value: int, /):
	"""Fired whenever the counter changes."""


def print_value(new_value: int):
	print("new value:", new_value)


counter_changed += print_value
counter_changed(42)
counter_changed -= print_value

Note that by default, @event requires you to make it explicit whether values are passed positionally or as keyword.

Class events

from typed_event import event


class Counter:
	def __init__(self):
		self._value = 0

	@event
	def changed(value: int, /):
		"""Fired when value changes."""

	def set_value(self, value: int):
		self._value = value
		self.changed(value)  # fire the event


c1 = Counter()
c2 = Counter()

def on_c1(value: int):
	print("c1:", value)

def on_c2(value: int):
	print("c2:", value)

c1.changed += on_c1
c2.changed += on_c2

c1.set_value(1)  # prints "c1: 1"
c2.set_value(2)  # prints "c2: 2"

Each instance gets its own bound event with its own listener list.

Defining event prototypes

Prototype restrictions:

  • No default argument values.
  • No *args / **kwargs in the event prototype.
  • Strict mode (enabled by default) requires parameters to be explicitly positional-only (/) or keyword-only (*). Opt-out by using @event(strict=False).

Example with explicit keyword-only parameters:

from typed_event import event


@event
def changed(*, a: int, b: int):
	pass


changed(a=1, b=2)      # ok
# changed(1, 2)         # TypeError

Notes:

  • Listener signatures are not validated at subscription time; a mismatch only fails when invoked.
  • Type annotations are not runtime-enforced — the name refers to static typing support, not runtime checks.

Cancellation

Raise CancelEvent in a listener to stop processing remaining listeners:

from typed_event import event, CancelEvent


@event
def changed(value: int, /):
	pass


def first(value: int):
	raise CancelEvent()


def second(value: int):
	print("never called")


changed += first
changed += second
changed(1)

Exception handling policy

Set via @event(exceptions=...):

  • "default" (default): passes exceptions to sys.excepthook.
  • "log": logs exceptions via logging.exception.
  • "raise": re-raises immediately and stops further listeners. I.e. no "handling" at all.
  • "group": runs all listeners, then raises ExceptionGroup if any failed.

CancelEvent is handled separately and is never treated as an error.

Return values

At most one non-None return value is allowed across prototype + listeners. If multiple handlers return a value, a RuntimeError is raised.

Changing event settings on module / project level

To define many events with changed settings (e.g. different default for exception), make an alias decorator like so:

from typed_event import event
my_event = event(strict=False, exceptions="log")

@my_event
def event1(): ...

@my_event
def event2(): ...

To apply project-wide, define the alias in a helper module and import from there.

Changelog

Version 1.1.0 (28.03.2026)

Make the library compatible with Python 3.10 (oldest version which is not EOL'ed yet.)

Version 1.0 (23.03.2026)

First release of the library as standalone project. Previously it was part of ASCII Designer.

Minor changes happened (strict mode as default, using sys.excepthook instead of print). Besides that, comprehensive testing and documentation was added.

License

Provided under MIT license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typed_event-1.1.0.tar.gz (41.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typed_event-1.1.0-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file typed_event-1.1.0.tar.gz.

File metadata

  • Download URL: typed_event-1.1.0.tar.gz
  • Upload date:
  • Size: 41.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for typed_event-1.1.0.tar.gz
Algorithm Hash digest
SHA256 569c71b16adf496aff311d174c082c7b5d15edd68522e165888017aa54a65cbc
MD5 202cb0c68a7c29b18ad1c97df0c542bd
BLAKE2b-256 9a3217629d40c05ed5613d4fc4747e34d3d5fbc223d4373a0d1e7b999dc20aa0

See more details on using hashes here.

File details

Details for the file typed_event-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: typed_event-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for typed_event-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 385e9b0fbccbc162832260c50de33a712a8144e44dadde4e7a3f5f617faeb147
MD5 3f47825c743966e10fe865f17c51f6a3
BLAKE2b-256 0b156ada4f58f615315b9946e224bf8a4c57bab8c98816568633278f0cf07ed0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page