Skip to content

Draft: Build Eventful 0.2 foundation#1

Open
aidankhogg wants to merge 1 commit into
dev/alphafrom
codex/transform-alpha-into-v1-foundation
Open

Draft: Build Eventful 0.2 foundation#1
aidankhogg wants to merge 1 commit into
dev/alphafrom
codex/transform-alpha-into-v1-foundation

Conversation

@aidankhogg

Copy link
Copy Markdown
Contributor

Motivation

  • Repair the alpha proof-of-concept so the package installs and imports with no extras and provide explicit, component-specific errors for optional integrations rather than hiding implementation errors.
  • Establish the v1 package topology and minimal provisional contracts so later 0.x releases can fill concrete integrations without changing top-level layout.
  • Add repository governance, documentation, CI and an annotation policy to make future work discoverable and tracked.
  • Gap inventory: the repo previously used src.eventful imports, misspelled adapter/persistence names, duplicate version sources and obsolete setup metadata, had undiscoverable tests and broad optional-ImportError swallowing; these were addressed or shimmed with deprecation warnings where needed.

Description

  • Packaging and metadata: consolidated packaging into pyproject.toml, made version single-source via src/eventful/__version__.py (__version__ = "0.2.0") and removed old setup.cfg/requirements.txt/setup.py artifacts.
  • Optional dependency handling: added src/eventful/_optional.py with require_optional and OptionalDependencyError so missing extras raise component-specific install guidance (e.g. RedisTransport, PostgresPersistence, FastAPI/Starlette adapters).
  • Fixed import surface and compatibility: replaced src.eventful imports with package imports and provided deprecation shims for misspellings (fastpi, postgres_perst) while keeping the in-memory bus API stable (eventful facade in src/eventful/__init__.py).
  • v1 topology and contracts: added eventful.contracts Protocols and experimental namespaces eventful.codecs, eventful.stores, eventful.transports, eventful.adapters, eventful.middleware, eventful.plugins, eventful.configuration, eventful.schemas, and eventful.observability with small example helpers (e.g. codecs/json.py, stores/memory.py).
  • Persistence and transports: repaired file_persistence uses and added experimental shells: transports/redis.py (raises OptionalDependencyError) and persistence/postgres_persistence.py (asyncpg guarded), plus a compatibility module persistence/postgres_perst.py that warns and re-exports.
  • Repo engineering and docs: added CI workflow (.github/workflows/ci.yml), pre-commit config, py.typed, ADRs, CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, SUPPORT.md, DEPRECATION.md, RELEASE.md, MkDocs config and doc pages (docs/), and an annotation checker script (scripts/check_annotations.py).
  • Tests and quality: made tests discoverable and added import-surface/docs-example tests (tests/test_import_surface.py, tests/test_docs_examples.py), corrected broken tests, adjusted bus/event behavior (queue/async handling and tag normalization) and aligned lint/mypy configs to be actionable for 0.2.

Testing

  • Unit tests: ran pytest -q (final run: 23 passed) after fixing tests and shims.
  • Static checks: ran ruff check . and mypy src/eventful with per-package overrides; both completed successfully in the final verification.
  • Documentation and packaging: built docs with mkdocs build --strict and built/installable distributions with python -m build and python -m pip install dist/*.whl; the installed package imports (import eventful; import eventful.contracts; import eventful.transports.redis; import eventful.persistence.postgres_persistence) succeeded and printed 0.2.0.
  • Annotation policy: ran python scripts/check_annotations.py to detect malformed or untracked annotations and fixed findings.

Note: git push origin codex/0.2-foundation could not complete from this environment because no origin remote is configured; the local branch commit is beed67b.


Codex Task

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c2e668529

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pyproject.toml
Issues = "https://github.com/Forebase/Eventful/issues"
test = ["build>=1.2", "pytest>=8.2", "pytest-asyncio>=0.23", "pytest-cov>=5"]
docs = ["mkdocs>=1.6"]
dev = ["build>=1.2", "mypy>=1.10", "pre-commit>=3.7", "pytest>=8.2", "pytest-asyncio>=0.23", "pytest-cov>=5", "ruff>=0.5"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include MkDocs in the CI install extra

In the CI workflow I checked, the job installs only python -m pip install -e '.[dev]' before invoking mkdocs build --strict, but this dev extra does not include mkdocs or the docs extra. On a fresh GitHub runner this makes every CI run fail at the docs step with mkdocs: command not found; add mkdocs to dev or install .[dev,docs] in the workflow.

Useful? React with 👍 / 👎.

Comment thread src/eventful/bus.py
Comment on lines 154 to +155
if not listeners:
return [] if async_ is False else asyncio.sleep(0, result=[])
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve an awaitable for async empty emits

When callers explicitly force async mode with async_=True, emitting an event that has no matching listeners now returns a plain list, so the documented async call pattern (await bus.emit(..., async_=True)) fails with TypeError: 'list' object can't be awaited for missing or filtered-out topics. Keep the no-listener path awaitable when async mode was requested while still returning [] for synchronous/default emits.

Useful? React with 👍 / 👎.

Comment thread src/eventful/_optional.py
Comment on lines +25 to +28
except ModuleNotFoundError as exc:
if exc.name == package:
raise OptionalDependencyError(component, extra, package) from exc
raise

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle missing top-level packages for dotted imports

For optional imports passed as submodules, such as the Starlette adapter's starlette.requests, ModuleNotFoundError.name is the missing top-level package (starlette) when the extra is not installed, so this equality check re-raises a raw ModuleNotFoundError instead of the new OptionalDependencyError. Users without the starlette extra therefore lose the precise install hint; compare against the top-level package name as well before re-raising.

Useful? React with 👍 / 👎.

@aidankhogg

Copy link
Copy Markdown
Contributor Author

@codex Fix the actionable issues identified below.

Work only in this pull request’s current head branch.

Requirements:

  • Do not create another repository.
  • Do not create another branch.
  • Do not open a new pull request.
  • Commit and push the fixes directly to the existing PR branch.
  • Preserve the PR’s existing scope.
  • Add or update tests for each fix.
  • Run the relevant test, lint, and type-check suites.
  • Report exactly what changed and any findings you did not resolve.

Tasks:

  1. Preserve an awaitable for async empty emits

When callers explicitly force async mode with async_=True, emitting an event that has no matching listeners now returns a plain list, so the documented async call pattern (await bus.emit(..., async_=True)) fails with TypeError: 'list' object can't be awaited for missing or filtered-out topics. Keep the no-listener path awaitable when async mode was requested while still returning [] for synchronous/default emits.

  1. andle missing top-level packages for dotted imports

For optional imports passed as submodules, such as the Starlette adapter's starlette.requests, ModuleNotFoundError.name is the missing top-level package (starlette) when the extra is not installed, so this equality check re-raises a raw ModuleNotFoundError instead of the new OptionalDependencyError. Users without the starlette extra therefore lose the precise install hint; compare against the top-level package name as well before re-raising.

@aidankhogg

Copy link
Copy Markdown
Contributor Author

@codex Fix the actionable issues identified below.

Work only in this pull request’s current head branch.

Requirements:

Do not create another repository.
Do not create another branch.
Do not open a new pull request.
Commit and push the fixes directly to the existing PR branch.
Preserve the PR’s existing scope.
Add or update tests for each fix.
Run the relevant test, lint, and type-check suites.
Report exactly what changed and any findings you did not resolve.
Tasks:

  1. Include MkDocs in the CI install extra

In the CI workflow I checked, the job installs only python -m pip install -e '.[dev]' before invoking mkdocs build --strict, but this dev extra does not include mkdocs or the docs extra. On a fresh GitHub runner this makes every CI run fail at the docs step with mkdocs: command not found; add mkdocs to dev or install .[dev,docs] in the workflow.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • Preserved async behavior for empty emits: EventBus.emit(..., async_=True) now returns the async emit coroutine even when there are no listeners, while default/synchronous empty emits still return []. src/eventful/bus.pyL154-L164
  • Updated optional dependency handling so dotted imports like starlette.requests are converted to OptionalDependencyError when the missing module is the top-level package (starlette). src/eventful/_optional.pyL21-L29
  • Added regression coverage for forced-async empty emits and default synchronous empty emits. tests/test_bus.pyL61-L74
  • Added regression coverage for dotted optional imports missing their top-level package. tests/test_import_surface.pyL38-L48
  • Committed the changes on the current branch with commit d394361 (Fix async empty emits and optional import hints).
  • Prepared PR metadata with the make_pr tool as required by the environment instructions.

Testing

  • python -m pip install -e '.[dev]'
  • pytest -q
  • ruff check .
  • mypy src/eventful
  • git diff --check
  • ⚠️ git push — push could not complete because this checkout has no configured push destination / remote.

Unresolved findings

  • I could not push the commit from this environment because git push reported no configured push destination.

View task →

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • Added mkdocs>=1.6 to the dev optional dependency extra so CI’s existing python -m pip install -e '.[dev]' step installs the mkdocs executable before mkdocs build --strict. pyproject.tomlL24-L33
  • Added a regression test that parses pyproject.toml and verifies the dev extra includes MkDocs for docs builds. tests/test_project_metadata.pyL1-L11
  • Committed the fix locally as 9cd73c0 Add MkDocs to dev extra.
  • Push was not completed because this checkout has no configured push destination/remote.

Testing

  • python -m pip install -e '.[dev]'
  • ruff check .
  • mypy src/eventful
  • pytest -q
  • mkdocs build --strict
  • ⚠️ git push (warning due to missing configured push destination/remote)

View task →

@aidankhogg

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c2e668529

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/eventful/bus.py
Comment thread .github/workflows/ci.yml
Comment thread src/eventful/adapters/starlette.py
Comment thread src/eventful/contracts/__init__.py
@aidankhogg

Copy link
Copy Markdown
Contributor Author

@codex Fix the actionable issues identified below.

Work only in this pull request’s current head branch.

Requirements:

Do not create another repository.
Do not create another branch.
Do not open a new pull request.
Commit and push the fixes directly to the existing PR branch.
Preserve the PR’s existing scope.
Add or update tests for each fix.
Run the relevant test, lint, and type-check suites.
Report exactly what changed and any findings you did not resolve.
Tasks:

  1. Preserve awaitable result when async emit has no listeners

When callers force async mode (async_=True) and an event has no matching listeners, this now returns a plain list, even though the method still advertises an awaitable in async mode and the previous branch returned an awaitable empty result. Async code that consistently does await bus.emit(..., async_=True) will now raise TypeError: object list can't be used in 'await' expression for empty topics; return an awaitable for the forced-async path.

  1. Install docs dependencies before running mkdocs

The workflow installs only .[dev] here, but I checked pyproject.toml and mkdocs is only in the separate docs extra, while the workflow later runs mkdocs build --strict. A fresh GitHub runner will therefore reach the docs step without the mkdocs command after this install; include the docs extra or add MkDocs to dev before invoking it.

  1. Convert missing Starlette into OptionalDependencyError

With Starlette absent, import_module('starlette.requests') raises ModuleNotFoundError with name == 'starlette', so the helper's exc.name == package check does not convert this import into the component-specific OptionalDependencyError. Users importing eventful.adapters.starlette without the extra therefore see a raw ModuleNotFoundError instead of the install hint used by the other optional integrations; pass or check the top-level package separately.

  1. Keep EventBusContract compatible with EventBus

EventBusContract is described as compatible with the legacy EventBus, but inheriting Publisher makes a publish() method mandatory while the shipped buses only provide emit(), register(), and unregister(). Runtime protocol checks reject the library's own bus (isinstance(EventBus(), EventBusContract) is false), so extension authors following this contract cannot accept the default bus without an adapter.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • Preserved awaitable behavior for forced async empty emits: EventBus.emit(..., async_=True) now returns the async emit coroutine even when no listeners match, while default/synchronous empty emits still return []. src/eventful/bus.pyL154-L166
  • Updated CI to install the docs extra with dev dependencies before running mkdocs build --strict. .github/workflows/ci.ymlL13-L22
  • Converted dotted optional imports with missing top-level packages into OptionalDependencyError by checking both the full requested package and its top-level package. src/eventful/_optional.pyL21-L29
  • Kept EventBusContract compatible with the shipped EventBus by removing the inherited publish() requirement and making the local dispatch contract require emit(), register(), and unregister(). src/eventful/contracts/init.pyL30-L35
  • Added regression tests for forced-async empty emits and default synchronous empty emits. tests/test_bus.pyL95-L107
  • Added regression tests for dotted optional dependency handling, CI docs-extra installation, and runtime compatibility between EventBus and EventBusContract. tests/test_import_surface.pyL38-L43 tests/test_ci_workflow.pyL6-L10 tests/test_contracts.pyL7-L8
  • Committed the changes on the current branch with commit 2e5a483 (Address PR review fixes).
  • Prepared PR metadata with the make_pr tool as required by the environment instructions.

Testing

  • python -m pip install -e '.[dev,docs]'
  • git diff --check
  • ruff check .
  • mypy src/eventful
  • pytest -q
  • mkdocs build --strict
  • python scripts/check_annotations.py
  • python -m build
  • ⚠️ git push — push could not complete because this checkout has no configured push destination/remote.

Unresolved findings

  • I could not push the local commit from this environment because git push reported that no push destination/remote is configured.

View task →

@aidankhogg

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c2e668529

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
- run: python -m pip install --force-reinstall dist/*.whl
- run: python -c "import eventful; import eventful.contracts"
- run: python -c "import eventful.transports.redis; import eventful.persistence.postgres_persistence"
- run: mkdocs build --strict

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Install the docs extra before running MkDocs

This workflow runs mkdocs build --strict, but the only install step uses .[dev]; in pyproject.toml, mkdocs is listed only under the docs extra, not dev. On a fresh ubuntu-latest runner, that leaves no mkdocs executable and the new CI job fails before validating docs; install .[dev,docs] or add MkDocs to dev.

Useful? React with 👍 / 👎.

Comment thread src/eventful/bus.py
Comment on lines 154 to +155
if not listeners:
return [] if async_ is False else asyncio.sleep(0, result=[])
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve awaitable results when async emission has no listeners

When callers force async mode with async_=True and the event has no matching listeners, this new early return now returns a plain list, so existing await bus.emit(..., async_=True) code raises TypeError: object list can't be used in 'await' expression. Keep the no-listener fast path synchronous only when async mode is not requested, and return an awaitable for the explicit async case.

Useful? React with 👍 / 👎.

Comment thread src/eventful/_optional.py
try:
return import_module(package)
except ModuleNotFoundError as exc:
if exc.name == package:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Wrap missing top-level packages for nested optional imports

For nested optional imports such as require_optional("Starlette adapter", "starlette", "starlette.requests"), a missing Starlette install raises ModuleNotFoundError with exc.name == "starlette", so this equality check falls through and users do not get the advertised OptionalDependencyError with the extra to install. Compare against the top-level package name, or pass it separately, so missing optional extras are consistently reported.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant