Skip to content
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

chore: fix pyee import, maintain backwards compat #140

Merged

Conversation

mikejgray
Copy link
Contributor

@mikejgray mikejgray commented Nov 20, 2024

#130

Summary by CodeRabbit

  • New Features

    • Introduced a new method run_in_thread in the MessageBusClient for concurrent websocket handling.
  • Improvements

    • Enhanced import handling for better compatibility with different versions of the pyee library.
    • Updated the emit method to ensure messages are sent only after confirming connection establishment.
    • Improved the GitHub Actions workflow for running unit tests, including updates to Python versions and package compatibility checks.
    • Added error handling for import failures in test files.
  • Bug Fixes

    • Improved error handling in the message emission process.

Copy link
Contributor

coderabbitai bot commented Nov 20, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes in this pull request introduce a conditional import mechanism for the ExecutorEventEmitter class from the pyee library, enhancing compatibility with various library versions. A new method, run_in_thread, is added to the MessageBusClient class to allow concurrent operation of the websocket handling. The emit method in both MessageBusClient and GUIWebsocketClient classes is updated to ensure messages are sent only after confirming a connection. Additionally, the requirements.txt file is modified to adjust the version constraints for the pyee package.

Changes

File Change Summary
ovos_bus_client/client/client.py - Added method run_in_thread in MessageBusClient.
- Updated emit method signature in MessageBusClient and GUIWebsocketClient.
- Implemented conditional import for ExecutorEventEmitter.
requirements.txt - Updated pyee version constraint from >=8.1.0, <12.0.0 to >=8.1.0, <13.0.0.
test/unittests/test_client.py - Modified import statement for ExecutorEventEmitter to use try-except for compatibility.
test/unittests/test_event_scheduler.py - Modified import statement for ExecutorEventEmitter to use try-except for compatibility.
.github/workflows/unit_tests.yml - Updated GitHub Actions workflow for unit tests, including Python version matrix and steps for pyee compatibility.

Possibly related PRs

  • chore: fix pyee import, maintain backwards compat #140: This PR introduces a conditional import for the ExecutorEventEmitter class from the pyee library, which is directly related to the changes made in the main PR regarding the same import mechanism. It also includes modifications to the MessageBusClient class, including the addition of the run_in_thread method, which aligns with the changes in the main PR.
  • fix:allow newer pyee #124: This PR updates the version constraint for the pyee package in requirements.txt, which is relevant to the changes in the main PR that also modify the version constraint for pyee.

Suggested reviewers

  • goldyfruit
  • mikejgray
  • NeonDaniel
  • builderjer

Poem

In a world of threads and events,
A rabbit hops with great intents.
With imports safe and messages clear,
We dance in code, without a fear.
So let us run and never stall,
For every change, we heed the call! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (5)
test/unittests/test_event_scheduler.py (1)

Line range hint 19-19: Address the TODO comment

The comment indicates this test should be moved to ovos-bus-client. Since this PR is touching this file, it might be a good opportunity to handle this TODO.

Would you like me to help create a GitHub issue to track moving these tests to ovos-bus-client?

test/unittests/test_client.py (1)

16-19: Add a comment explaining the compatibility reason

Consider adding a comment to explain why this conditional import is necessary. This will help future maintainers understand the purpose of this pattern.

+# Handle compatibility with different pyee versions
+# pyee >= 8.1.0 moved ExecutorEventEmitter to pyee.executor
 try:
     from pyee import ExecutorEventEmitter
 except ImportError:
     from pyee.executor import ExecutorEventEmitter
ovos_bus_client/client/client.py (3)

Line range hint 214-222: Enhance error logging for failed message emission

The error message could be more specific by including the actual exception details.

-            LOG.exception(f"failed to emit message {message.msg_type} with len {len(msg)}")
+            LOG.exception(f"Failed to emit message {message.msg_type} (length: {len(msg)}): {str(e)}")

Line range hint 449-454: Add docstring explaining daemon thread behavior

The method correctly implements threading, but should document the daemon behavior.

 def run_in_thread(self):
-    """Launches the run_forever in a separate daemon thread."""
+    """Launches the run_forever method in a separate daemon thread.
+    
+    The thread is created as a daemon thread, which means it will not prevent
+    the program from exiting when the main thread ends. This is useful for
+    background tasks that should not block program termination.
+    
+    Returns:
+        Thread: The created daemon thread instance
+    """
     t = Thread(target=self.run_forever)
     t.daemon = True
     t.start()
     return t

Line range hint 484-502: Consider refactoring to reduce code duplication with parent class

The connection check and WebSocket exception handling is duplicated from MessageBusClient.emit. Consider extracting these common patterns into protected methods.

 class MessageBusClient(_MessageBusClientBase):
+    def _ensure_connected(self):
+        """Ensure websocket connection is established."""
+        if not self.connected_event.wait(10):
+            if not self.started_running:
+                raise ValueError('You must execute run_forever() '
+                               'before emitting messages')
+            self.connected_event.wait()
+
+    def _send_message(self, msg):
+        """Send message with exception handling."""
+        try:
+            self.client.send(msg)
+        except WebSocketConnectionClosedException:
+            LOG.warning('Could not send message because connection has been closed')
+        except Exception as e:
+            LOG.exception(f"Failed to emit message: {str(e)}")

     def emit(self, message: Message):
-        if not self.connected_event.wait(10):
-            if not self.started_running:
-                raise ValueError('You must execute run_forever() '
-                               'before emitting messages')
-            self.connected_event.wait()
+        self._ensure_connected()
         # ... session context code ...
         if hasattr(message, 'serialize'):
             msg = message.serialize()
         else:
             msg = orjson.dumps(message.__dict__).decode("utf-8")
-        try:
-            self.client.send(msg)
-        except WebSocketConnectionClosedException:
-            LOG.warning('Could not send message because connection has been closed')
-        except Exception as e:
-            LOG.exception(f"Failed to emit message: {str(e)}")
+        self._send_message(msg)

 class GUIWebsocketClient(MessageBusClient):
     def emit(self, message: GUIMessage):
-        if not self.connected_event.wait(10):
-            if not self.started_running:
-                raise ValueError('You must execute run_forever() '
-                               'before emitting messages')
-            self.connected_event.wait()
+        self._ensure_connected()
         if hasattr(message, 'serialize'):
             msg = message.serialize()
         else:
             msg = orjson.dumps(message.__dict__).decode("utf-8")
-        try:
-            self.client.send(msg)
-        except WebSocketConnectionClosedException:
-            LOG.warning('Could not send message because connection has been closed')
-        except Exception as e:
-            LOG.exception(f"Failed to emit message: {str(e)}")
+        self._send_message(msg)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 942f63c and 6513cb5.

📒 Files selected for processing (4)
  • ovos_bus_client/client/client.py (1 hunks)
  • requirements.txt (1 hunks)
  • test/unittests/test_client.py (1 hunks)
  • test/unittests/test_event_scheduler.py (1 hunks)
🔇 Additional comments (4)
requirements.txt (2)

4-4: LGTM! Version constraint update maintains backwards compatibility

The relaxed upper bound allows for newer pyee versions while maintaining the same lower bound, which aligns with the PR's compatibility objectives.


4-4: Verify compatibility with pyee 12.x versions

Since we're extending support to include pyee 12.x versions, let's verify there are no breaking changes that could affect the conditional import mechanism.

✅ Verification successful

Let me analyze the import mechanism further by checking the actual usage of ExecutorEventEmitter.


Extending pyee support to v12.x is safe

The codebase handles pyee imports with a robust fallback mechanism that accommodates both old and new import paths for ExecutorEventEmitter. The usage is straightforward and limited to basic event emitter functionality, which has remained stable across pyee versions. The conditional import pattern:

try:
    from pyee import ExecutorEventEmitter
except ImportError:
    from pyee.executor import ExecutorEventEmitter

ensures compatibility with both pre-8.x and post-8.x versions of pyee, and this pattern continues to work in v12.x.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential breaking changes in pyee 12.x and verify the conditional import mechanism

# Check release notes and changes between versions
curl -s https://pypi.org/pypi/pyee/json | jq -r '.releases | keys[]' | grep "^12\." 

# Look for ExecutorEventEmitter usage in the codebase
rg -l "ExecutorEventEmitter"

# Check for any conditional imports of ExecutorEventEmitter
rg "from pyee" -A 2

Length of output: 1199


Script:

#!/bin/bash
# Check how ExecutorEventEmitter is used in the codebase
rg "ExecutorEventEmitter" -B 2 -A 5

# Check if there are any specific pyee version checks or compatibility handling
rg "pyee" -B 2 -A 5

Length of output: 6997

test/unittests/test_event_scheduler.py (1)

7-11: LGTM! Import change maintains backward compatibility

The conditional import mechanism correctly handles compatibility with different versions of the pyee library by attempting to import from the new location first, then falling back to the legacy path.

Let's verify the pyee package structure across versions:

✅ Verification successful

Import changes are necessary and correctly implemented

The verification confirms this change is well-justified:

  • The project supports pyee versions 8.1.0 to 12.x.x (pyee>= 8.1.0, < 13.0.0)
  • The import changes are consistently implemented across all files using ExecutorEventEmitter:
    • ovos_bus_client/client/client.py
    • test/unittests/test_client.py
    • test/unittests/test_event_scheduler.py

The try-except pattern maintains compatibility across supported pyee versions where the import location changed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the import paths for ExecutorEventEmitter across different pyee versions
# Expected: Different import paths in different versions, confirming the need for this change

# Check if pyee is listed in requirements.txt and get its version constraint
rg -l "pyee" "requirements.txt"

# Look for both import patterns in the codebase to ensure consistency
echo "Checking new import pattern..."
rg "from pyee import ExecutorEventEmitter"

echo "Checking legacy import pattern..."
rg "from pyee.executor import ExecutorEventEmitter"

Length of output: 776


Script:

#!/bin/bash
# Let's check the version constraint in requirements.txt to understand if this change is needed
rg "pyee" requirements.txt -A 1 -B 1

# Also check if there are any other files that might be importing from pyee
# to ensure we haven't missed any cases
rg "import.*pyee" --type py

Length of output: 121

ovos_bus_client/client/client.py (1)

10-14: LGTM: Robust backwards-compatible import handling

The conditional import correctly handles different versions of the pyee library where ExecutorEventEmitter location changed, maintaining backwards compatibility as intended.

test/unittests/test_client.py Outdated Show resolved Hide resolved
@mikejgray mikejgray changed the title fix import, maintain backwards compat chore: fix pyee import, maintain backwards compat Nov 20, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
.github/workflows/unit_tests.yml (1)

56-56: LGTM! Good backwards compatibility testing

The dual testing approach with different pyee versions is well implemented. Consider adding version outputs for better test logs:

 pip install -U pyee<12.0.0
+pip show pyee
 pytest --cov=ovos_bus_client --cov-report=xml test/unittests
 pip install -U pyee>12.0.0
+pip show pyee
 pytest --cov=ovos_bus_client --cov-report=xml --cov-append test/unittests

Also applies to: 63-66

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0e774b8 and d3abe23.

📒 Files selected for processing (1)
  • .github/workflows/unit_tests.yml (2 hunks)
🔇 Additional comments (4)
.github/workflows/unit_tests.yml (4)

7-15: LGTM! Path ignores are appropriate

The ignored paths are non-functional files that don't require CI validation on PR.


40-42: LGTM! Action versions updated appropriately

Updates to the latest stable versions of actions improve security and functionality.


69-73: LGTM! Codecov configuration is well structured

The update to v5 and addition of verbose logging will improve coverage reporting transparency.


36-36: Consider the stability of Python 3.13

Python 3.13 is currently in development and may not be stable enough for production testing. Consider waiting until it reaches at least beta status.

✅ Verification successful

Python 3.13 is not used in any critical functionality

The codebase appears to be using Python 3.13 in the test matrix only, not in any core functionality. The test matrix includes multiple Python versions (3.9-3.13) to ensure forward compatibility, but there are no Python 3.13-specific features being used that would cause stability concerns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if any code uses features incompatible with Python 3.13
rg "async with" # New syntax changes in 3.13
rg "match.*case" # Pattern matching changes

Length of output: 36


Script:

#!/bin/bash
# Let's check the Python files and their dependencies
fd '\.py$' --exec cat {} \; # Show Python files content
fd 'requirements.*\.txt$' --exec cat {} \; # Show dependencies
fd 'pyproject\.toml$' --exec cat {} \; # Show project configuration
fd 'setup\.py$' --exec cat {} \; # Show setup configuration

Length of output: 235227

.github/workflows/unit_tests.yml Outdated Show resolved Hide resolved
@JarbasAl JarbasAl force-pushed the dependabot/pip/pyee-gte-8.1.0-and-lt-13.0.0 branch from 794f35f to 7a68b52 Compare November 21, 2024 07:14
Copy link

codecov bot commented Nov 21, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 42.19%. Comparing base (1db5975) to head (1987375).
Report is 156 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #140      +/-   ##
==========================================
+ Coverage   39.49%   42.19%   +2.69%     
==========================================
  Files          17       16       -1     
  Lines        1732     2164     +432     
==========================================
+ Hits          684      913     +229     
- Misses       1048     1251     +203     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (4)
test/unittests/test_event_scheduler.py (1)

7-11: LGTM! The conditional import enhances compatibility.

The try-except block for importing ExecutorEventEmitter aligns with the PR's objective of maintaining backwards compatibility with different versions of the pyee library.

Consider adding a brief comment explaining why this conditional import is necessary:

+# Handle different pyee versions - ExecutorEventEmitter location changed in newer versions
try:
    from pyee import ExecutorEventEmitter
except (ImportError, ModuleNotFoundError):
    from pyee.executor import ExecutorEventEmitter
🧰 Tools
🪛 Ruff

10-10: pyee.executor.ExecutorEventEmitter imported but unused

Remove unused import: pyee.executor.ExecutorEventEmitter

(F401)

ovos_bus_client/client/client.py (3)

Line range hint 371-376: Consider adding error handling for thread creation.

The implementation looks good but could benefit from basic error handling.

Consider adding try-except block to handle potential threading errors:

 def run_in_thread(self):
     """Launches the run_forever in a separate daemon thread."""
-    t = Thread(target=self.run_forever)
-    t.daemon = True
-    t.start()
-    return t
+    try:
+        t = Thread(target=self.run_forever)
+        t.daemon = True
+        t.start()
+        return t
+    except RuntimeError as e:
+        LOG.error(f"Failed to start message bus thread: {e}")
+        raise
🧰 Tools
🪛 Ruff

9-9: ovos_utils.log.deprecated imported but unused

Remove unused import: ovos_utils.log.deprecated

(F401)


Line range hint 381-401: Consider enhancing error logging for debugging.

The implementation correctly handles connection checks and message serialization.

Consider adding more context to error logs:

         try:
             if hasattr(message, 'serialize'):
                 self.client.send(message.serialize())
             else:
                 self.client.send(orjson.dumps(message.__dict__).decode("utf-8"))
         except WebSocketConnectionClosedException:
-            LOG.warning('Could not send %s message because connection '
-                        'has been closed', message.msg_type)
+            LOG.warning('Could not send %s message (gui_id: %s) because connection '
+                        'has been closed', message.msg_type, self.gui_id)
🧰 Tools
🪛 Ruff

9-9: ovos_utils.log.deprecated imported but unused

Remove unused import: ovos_utils.log.deprecated

(F401)


Line range hint 403-418: Add error handling for message parsing.

While the implementation correctly handles GUI messages, it should handle potential parsing errors.

Consider adding error handling for message parsing:

         if len(args) == 1:
             message = args[0]
         else:
             message = args[1]
 
         self.emitter.emit('message', message)
 
-        parsed_message = GUIMessage.deserialize(message)
-        self.emitter.emit(parsed_message.msg_type, parsed_message)
+        try:
+            parsed_message = GUIMessage.deserialize(message)
+            self.emitter.emit(parsed_message.msg_type, parsed_message)
+        except Exception as e:
+            LOG.error(f"Failed to parse GUI message: {e}")
+            LOG.debug(f"Raw message: {message}")
🧰 Tools
🪛 Ruff

9-9: ovos_utils.log.deprecated imported but unused

Remove unused import: ovos_utils.log.deprecated

(F401)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 794f35f and 7a68b52.

📒 Files selected for processing (5)
  • .github/workflows/unit_tests.yml (2 hunks)
  • ovos_bus_client/client/client.py (1 hunks)
  • requirements.txt (1 hunks)
  • test/unittests/test_client.py (1 hunks)
  • test/unittests/test_event_scheduler.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/unit_tests.yml
  • requirements.txt
  • test/unittests/test_client.py
🧰 Additional context used
🪛 Ruff
test/unittests/test_event_scheduler.py

10-10: pyee.executor.ExecutorEventEmitter imported but unused

Remove unused import: pyee.executor.ExecutorEventEmitter

(F401)

🔇 Additional comments (2)
test/unittests/test_event_scheduler.py (1)

7-11: Verify consistent import handling across the codebase.

Let's ensure this import pattern is consistently applied wherever ExecutorEventEmitter is used.

✅ Verification successful

Import handling is consistent across the codebase

The verification shows that the same try-except import pattern for ExecutorEventEmitter is consistently implemented in all three files where it's used:

  • test/unittests/test_event_scheduler.py
  • test/unittests/test_client.py
  • ovos_bus_client/client/client.py
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistency in ExecutorEventEmitter imports

# Find all Python files using ExecutorEventEmitter
echo "Files importing ExecutorEventEmitter:"
rg -l "ExecutorEventEmitter"

# Show the import patterns used
echo -e "\nImport patterns used:"
rg -B2 -A2 "ExecutorEventEmitter"

Length of output: 2389

🧰 Tools
🪛 Ruff

10-10: pyee.executor.ExecutorEventEmitter imported but unused

Remove unused import: pyee.executor.ExecutorEventEmitter

(F401)

ovos_bus_client/client/client.py (1)

10-14: LGTM! Robust import handling for pyee compatibility.

The conditional import correctly handles both old and new pyee package structures, maintaining backward compatibility.

Let's verify the pyee package structure:

✅ Verification successful

Verified: Import handling aligns with package requirements and usage

The conditional import is correctly implemented to handle pyee package structure changes:

  • Package requires pyee>= 8.1.0, < 13.0.0 which spans versions with both import styles
  • The same import pattern is consistently used across the codebase (main code and tests)
  • Tests mirror the same import handling, ensuring compatibility across different pyee versions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify pyee package structure across different versions

# Check if pyee is listed in requirements
rg -l "pyee" "requirements.*"

# Check for direct ExecutorEventEmitter imports
rg "from pyee import ExecutorEventEmitter"

# Check for executor module imports
rg "from pyee.executor import ExecutorEventEmitter"

Length of output: 666


Script:

#!/bin/bash
# Let's check the package dependencies in setup.py or pyproject.toml
fd "setup.py|pyproject.toml" --exec cat {}

# Also check if there are any version constraints for pyee in any other files
rg "pyee[<>=]" --type yaml --type txt --type python

Length of output: 2883


Script:

#!/bin/bash
# Check for requirements.txt in the root directory
fd "requirements.txt" --exec cat {}

# Let's also check for any pyee-related test imports and usage
rg "pyee" test/ -A 3

Length of output: 1041

@JarbasAl JarbasAl merged commit 85b4729 into OpenVoiceOS:dev Nov 21, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants