Draft: Build Eventful 0.2 foundation#1
Conversation
There was a problem hiding this comment.
💡 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".
| 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"] |
There was a problem hiding this comment.
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 👍 / 👎.
| if not listeners: | ||
| return [] if async_ is False else asyncio.sleep(0, result=[]) | ||
| return [] |
There was a problem hiding this comment.
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 👍 / 👎.
| except ModuleNotFoundError as exc: | ||
| if exc.name == package: | ||
| raise OptionalDependencyError(component, extra, package) from exc | ||
| raise |
There was a problem hiding this comment.
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 👍 / 👎.
|
@codex Fix the actionable issues identified below. Work only in this pull request’s current head branch. Requirements:
Tasks:
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.
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. |
|
@codex Fix the actionable issues identified below. Work only in this pull request’s current head branch. Requirements: Do not create another repository.
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. |
|
Summary
Testing
Unresolved findings
|
|
Summary
Testing
|
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@codex Fix the actionable issues identified below. Work only in this pull request’s current head branch. Requirements: Do not create another repository.
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.
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.
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.
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. |
|
Summary
Testing
Unresolved findings
|
|
@codex review |
There was a problem hiding this comment.
💡 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".
| - 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 |
There was a problem hiding this comment.
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 👍 / 👎.
| if not listeners: | ||
| return [] if async_ is False else asyncio.sleep(0, result=[]) | ||
| return [] |
There was a problem hiding this comment.
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 👍 / 👎.
| try: | ||
| return import_module(package) | ||
| except ModuleNotFoundError as exc: | ||
| if exc.name == package: |
There was a problem hiding this comment.
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 👍 / 👎.
Motivation
src.eventfulimports, 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
pyproject.toml, made version single-source viasrc/eventful/__version__.py(__version__ = "0.2.0") and removed oldsetup.cfg/requirements.txt/setup.pyartifacts.src/eventful/_optional.pywithrequire_optionalandOptionalDependencyErrorso missing extras raise component-specific install guidance (e.g.RedisTransport,PostgresPersistence, FastAPI/Starlette adapters).src.eventfulimports with package imports and provided deprecation shims for misspellings (fastpi,postgres_perst) while keeping the in-memory bus API stable (eventfulfacade insrc/eventful/__init__.py).eventful.contractsProtocols and experimental namespaceseventful.codecs,eventful.stores,eventful.transports,eventful.adapters,eventful.middleware,eventful.plugins,eventful.configuration,eventful.schemas, andeventful.observabilitywith small example helpers (e.g.codecs/json.py,stores/memory.py).file_persistenceuses and added experimental shells:transports/redis.py(raisesOptionalDependencyError) andpersistence/postgres_persistence.py(asyncpg guarded), plus a compatibility modulepersistence/postgres_perst.pythat warns and re-exports..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/test_import_surface.py,tests/test_docs_examples.py), corrected broken tests, adjustedbus/eventbehavior (queue/async handling and tag normalization) and aligned lint/mypy configs to be actionable for 0.2.Testing
pytest -q(final run:23 passed) after fixing tests and shims.ruff check .andmypy src/eventfulwith per-package overrides; both completed successfully in the final verification.mkdocs build --strictand built/installable distributions withpython -m buildandpython -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 printed0.2.0.python scripts/check_annotations.pyto detect malformed or untracked annotations and fixed findings.Note:
git push origin codex/0.2-foundationcould not complete from this environment because nooriginremote is configured; the local branch commit isbeed67b.Codex Task