-
Notifications
You must be signed in to change notification settings - Fork 25
fix: daemon mode wasn't reporting progress #264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
test/openjd/adaptor_runtime/integ/ProgressReportingAdaptor/ProgressReportingAdaptor.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {} |
5 changes: 5 additions & 0 deletions
5
test/openjd/adaptor_runtime/integ/ProgressReportingAdaptor/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
|
|
||
| from .adaptor import ProgressReportingAdaptor | ||
|
|
||
| __all__ = ["ProgressReportingAdaptor"] |
18 changes: 18 additions & 0 deletions
18
test/openjd/adaptor_runtime/integ/ProgressReportingAdaptor/__main__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
|
|
||
| import sys | ||
|
|
||
| from openjd.adaptor_runtime import EntryPoint | ||
| from .adaptor import ProgressReportingAdaptor | ||
|
|
||
|
|
||
| def main(): | ||
| package_name = vars(sys.modules[__name__])["__package__"] | ||
| if not package_name: | ||
| raise RuntimeError(f"Must be run as a module. Do not run {__file__} directly") | ||
|
|
||
| EntryPoint(ProgressReportingAdaptor).start() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
19 changes: 19 additions & 0 deletions
19
test/openjd/adaptor_runtime/integ/ProgressReportingAdaptor/adaptor.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
|
|
||
| from openjd.adaptor_runtime.adaptors import Adaptor, SemanticVersion | ||
|
|
||
|
|
||
| class ProgressReportingAdaptor(Adaptor): | ||
| """ | ||
| A minimal adaptor that calls update_status to report progress during on_run. | ||
| Used to test that progress messages propagate from the background adaptor | ||
| to the frontend process's stdout. | ||
| """ | ||
|
|
||
| @property | ||
| def integration_data_interface_version(self) -> SemanticVersion: | ||
| return SemanticVersion(major=0, minor=1) | ||
|
|
||
| def on_run(self, run_data: dict) -> None: | ||
| progress = run_data.get("progress", 50.0) | ||
| self.update_status(progress=progress) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
test/openjd/adaptor_runtime/integ/test_progress_in_background_mode.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
|
|
||
| """ | ||
| Test to reproduce a bug where progress reporting via update_status() does not | ||
| propagate from the background adaptor to the frontend process's stdout. | ||
|
|
||
| The flow is: | ||
| 1. Application outputs progress info | ||
| 2. Background adaptor (backend process) calls update_status(progress=X) | ||
| 3. update_status writes "openjd_progress: X" to sys.stdout | ||
| 4. Frontend process should emit "openjd_progress: X" on its own stdout | ||
| so the worker agent can detect it | ||
|
|
||
| The bug: In daemon/background mode, the backend process replaces its stream | ||
| handler with a LogBufferHandler. But update_status() writes directly to | ||
| sys.stdout (not through the logging framework), so the progress message | ||
| never reaches the log buffer, and thus never reaches the frontend. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| import os | ||
| import pathlib | ||
| import sys | ||
| from logging import INFO | ||
| from unittest.mock import patch | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
|
|
||
| import openjd.adaptor_runtime._entrypoint as runtime_entrypoint | ||
| from openjd.adaptor_runtime import EntryPoint | ||
|
|
||
| mod_path = Path(__file__).parent.resolve() | ||
| sys.path.append(str(mod_path)) | ||
|
|
||
| if (_pypath := os.environ.get("PYTHONPATH")) is not None: | ||
| os.environ["PYTHONPATH"] = os.pathsep.join([_pypath, str(mod_path)]) | ||
| else: | ||
| os.environ["PYTHONPATH"] = str(mod_path) | ||
|
|
||
| from ProgressReportingAdaptor import ProgressReportingAdaptor # noqa: E402 | ||
|
|
||
|
|
||
| class TestProgressReportingInDaemonMode: | ||
| """ | ||
| Tests that progress messages from update_status() in the background adaptor | ||
| are propagated to the frontend process's stdout so the worker agent can | ||
| detect them. | ||
| """ | ||
|
|
||
| def test_progress_propagates_to_frontend_stdout( | ||
| self, | ||
| capfd: pytest.CaptureFixture, | ||
| caplog: pytest.LogCaptureFixture, | ||
| tmp_path: pathlib.Path, | ||
| ): | ||
| """ | ||
| Reproduces the bug: when running in daemon mode, calling | ||
| update_status(progress=50) in the backend adaptor should result in | ||
| "openjd_progress: 50" appearing on the frontend's stdout. | ||
|
|
||
| The worker agent monitors the frontend process's stdout for these | ||
| messages. If they don't appear, the worker never knows about progress. | ||
| """ | ||
| caplog.set_level(INFO) | ||
| connection_file = tmp_path / "connection.json" | ||
| entrypoint = EntryPoint(ProgressReportingAdaptor) | ||
|
|
||
| # Start the daemon | ||
| start_argv = [ | ||
| "program_filename.py", | ||
| "daemon", | ||
| "start", | ||
| "--connection-file", | ||
| str(connection_file), | ||
| ] | ||
| with ( | ||
| patch.object(runtime_entrypoint.sys, "argv", start_argv), | ||
| patch.object(runtime_entrypoint.logging.Logger, "setLevel"), | ||
| ): | ||
| entrypoint.start() | ||
|
|
||
| # Run with progress=50 | ||
| run_argv = [ | ||
| "program_filename.py", | ||
| "daemon", | ||
| "run", | ||
| "--connection-file", | ||
| str(connection_file), | ||
| "--run-data", | ||
| json.dumps({"progress": 50.0}), | ||
| ] | ||
| with ( | ||
| patch.object(runtime_entrypoint.sys, "argv", run_argv), | ||
| patch.object(runtime_entrypoint.logging.Logger, "setLevel"), | ||
| ): | ||
| entrypoint.start() | ||
|
|
||
| # Stop the daemon | ||
| stop_argv = [ | ||
| "program_filename.py", | ||
| "daemon", | ||
| "stop", | ||
| "--connection-file", | ||
| str(connection_file), | ||
| ] | ||
| with ( | ||
| patch.object(runtime_entrypoint.sys, "argv", stop_argv), | ||
| patch.object(runtime_entrypoint.logging.Logger, "setLevel"), | ||
| ): | ||
| entrypoint.start() | ||
|
|
||
| # Verify that "openjd_progress: 50" appeared on stdout. | ||
| # The worker agent is watching stdout for this pattern. | ||
| captured = capfd.readouterr() | ||
| assert "openjd_progress: 50" in captured.out, ( | ||
| f"Expected 'openjd_progress: 50' on stdout but got:\n{captured.out}\n\n" | ||
| f"Log output:\n{caplog.text}" | ||
| ) | ||
|
|
||
| def test_progress_works_in_run_mode( | ||
| self, | ||
| capfd: pytest.CaptureFixture, | ||
| caplog: pytest.LogCaptureFixture, | ||
| ): | ||
| """ | ||
| Sanity check: progress reporting works fine in non-daemon 'run' mode | ||
| because update_status writes directly to sys.stdout and the worker | ||
| agent is directly reading from that process's stdout. | ||
| """ | ||
| caplog.set_level(INFO) | ||
| entrypoint = EntryPoint(ProgressReportingAdaptor) | ||
|
|
||
| run_argv = [ | ||
| "program_filename.py", | ||
| "run", | ||
| "--run-data", | ||
| json.dumps({"progress": 75.0}), | ||
| ] | ||
| with ( | ||
| patch.object(runtime_entrypoint.sys, "argv", run_argv), | ||
| patch.object(runtime_entrypoint.logging.Logger, "setLevel"), | ||
| ): | ||
| entrypoint.start() | ||
|
|
||
| captured = capfd.readouterr() | ||
| assert ( | ||
| "openjd_progress: 75" in captured.out | ||
| ), f"Expected 'openjd_progress: 75' on stdout but got:\n{captured.out}" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is just purely for testing right ?