Skip to content

♻️ Refactor - #55

Open
yhs0602 wants to merge 1 commit into
mainfrom
refactor/code-quality
Open

♻️ Refactor#55
yhs0602 wants to merge 1 commit into
mainfrom
refactor/code-quality

Conversation

@yhs0602

@yhs0602 yhs0602 commented Jan 26, 2026

Copy link
Copy Markdown
Owner

Pull

Coverage Badge

Summary by CodeRabbit

  • Improvements

    • Enhanced error handling with clearer error messages for port and configuration issues.
    • Improved connection retry logic with refined timeout handling.
    • Added configuration validation for image dimensions at initialization.
  • Tests

    • Added unit tests for environment lifecycle and IPC socket communication.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 26, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR introduces infrastructure improvements for the CraftGround environment: a constants module for configuration values, a custom exception hierarchy, enhanced port validation and connection timeout handling in socket IPC, validation for image dimensions, platform-specific process termination logic with comprehensive error handling, and updated API signatures with docstrings. Additionally, new unit tests validate configuration and IPC behavior.

Changes

Cohort / File(s) Summary
Configuration & Gitignore
.gitignore, src/craftground/MinecraftEnv/.gitignore
Added CODE_REVIEW.md and minecraft_sources/ to respective gitignore files for version control exclusion.
Constants Module
src/craftground/constants.py
New module defining 12 constants for environment defaults (image dimensions, port, FOV), connection retry/timeout parameters, process termination timeout, and port/image size bounds.
Exception Hierarchy
src/craftground/exceptions.py
New module establishing custom exception classes: base CraftGroundError, and specialized subclasses (ProcessTerminationError, ConnectionTimeoutError, ConfigurationError, PortInUseError, InvalidPortError, InvalidImageSizeError).
Action Space Refactoring
src/craftground/environment/action_space.py
Refactored action_v2_dict_to_message to use iterative loops for populating boolean and hotbar fields instead of explicit assignments; added docstring; maintains backward compatibility with safe defaults.
Socket IPC Enhancements
src/craftground/environment/socket_ipc.py
Added port validation with range checks using MIN_PORT and MAX_PORT; introduced InvalidPortError and PortInUseError exceptions; refactored connection retry logic using MAX_CONNECTION_RETRIES and CONNECTION_TIMEOUT constants; added comprehensive docstrings; updated send_fastreset2 signature to accept Optional[List[str]].
Environment Core Refactoring
src/craftground/environment/environment.py
Implemented platform-specific process termination (_terminate_unix, _terminate_windows, _force_kill); updated reset(), step(), and convert_observation_v2() signatures with docstrings and enhanced return types; improved error handling with new exception types and logging.
Configuration Validation
src/craftground/initial_environment_config.py
Added internal _validate() method enforcing image size bounds (MIN_IMAGE_SIZE, MAX_IMAGE_SIZE); raises InvalidImageSizeError on violations during initialization.
Unit Tests
tests/python/unit/test_environment_lifecycle.py, tests/python/unit/test_ipc_mock.py
New test suites validating environment initialization, configuration validation with invalid image sizes, port validation errors, action sending via IPC, and socket liveness checks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop hop, the groundwork's now in place,
Constants, exceptions—structure with grace!
Platform-aware termination takes the lead,
Validation and tests fulfill every need!
Craftground's foundation now stands firm and bright,

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description only contains a coverage badge template placeholder with no actual content explaining the changes, objectives, or rationale for the refactoring work. Add a detailed description explaining the key changes: new constants module, exception hierarchy, environment lifecycle refactoring, port validation enhancements, and test additions with their purposes.
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title '♻️ Refactor' is vague and generic, failing to convey the specific nature of changes across multiple files including new constants, exceptions, environment refactoring, and test additions. Replace with a more descriptive title that summarizes the primary change, such as 'Add constants, exceptions, and refactor environment lifecycle handling' or 'Refactor environment lifecycle with port validation and exception hierarchy'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/craftground/environment/socket_ipc.py`:
- Around line 98-101: The PortInUseError message is using the
already-incremented port value, making "starting from {port}" inaccurate; change
the check in the loop that raises PortInUseError (where MAX_PORT and port are
referenced) to report the original starting port (store the initial port in a
variable like start_port before incrementing) and use that start_port in the
PortInUseError message so it matches the actual requested start value (reference
the port variable, MAX_PORT, and PortInUseError in your fix).
- Around line 281-284: The premature timeout comes from the extra next_output >
MAX_CONNECTION_RETRIES check and the raised ConnectionTimeoutError lacks
exception chaining; update the logic in socket_ipc.py (look for next_output,
wait_time, MAX_CONNECTION_RETRIES, and ConnectionTimeoutError) by removing the
redundant next_output > MAX_CONNECTION_RETRIES branch so the loop relies solely
on wait_time < MAX_CONNECTION_RETRIES for termination, and when you do raise
ConnectionTimeoutError include the original exception with "raise
ConnectionTimeoutError(...) from err" (capture the original exception variable
where the wait/connection error occurred) to preserve the exception chain.
- Around line 77-80: The error message uses the incremented port value instead
of the original starting port; capture the initial starting port (e.g.,
start_port = port) before the loop that increments port and then, when raising
PortInUseError, use that preserved start_port in the message (keep the
PortInUseError raise site and MAX_PORT check unchanged but swap the f-string to
reference start_port).
🧹 Nitpick comments (6)
tests/python/unit/test_environment_lifecycle.py (2)

13-19: Missing environment cleanup after test.

The test creates an environment but doesn't close it, which may leak resources (sockets, processes). Consider using a fixture or explicit cleanup.

♻️ Suggested fix
     def test_environment_creation(self):
         """Test that environment can be created."""
         config = InitialEnvironmentConfig(image_width=64, image_height=64)
         env = make(initial_env_config=config, verbose=False)
-        assert env is not None
-        assert env.observation_space is not None
-        assert env.action_space is not None
+        try:
+            assert env is not None
+            assert env.observation_space is not None
+            assert env.action_space is not None
+        finally:
+            env.close()

21-33: Consider adding height validation test cases.

The invalid size tests focus on image_width. For symmetry and complete coverage, consider adding test cases for invalid image_height values as well.

♻️ Suggested addition
        # Invalid height - too small
        with pytest.raises(InvalidImageSizeError):
            InitialEnvironmentConfig(image_width=64, image_height=0)

        # Invalid height - too large
        with pytest.raises(InvalidImageSizeError):
            InitialEnvironmentConfig(image_width=64, image_height=10000)
src/craftground/initial_environment_config.py (1)

133-139: Consider reordering validation and kwargs check.

Currently, _validate() is called before the unknown kwargs warning. If validation fails, users won't see any warning about unexpected parameters. Consider swapping the order so users get all feedback.

♻️ Suggested reorder
-        # Validate configuration
-        self._validate()
-
         # Check for unknown kwargs
         if kwargs:
             print(f"Unexpected Kwargs: {kwargs}")
+
+        # Validate configuration
+        self._validate()
tests/python/unit/test_ipc_mock.py (3)

10-14: Unused import: ConnectionTimeoutError.

ConnectionTimeoutError is imported but not used in any test. Remove it to keep imports clean.

♻️ Suggested fix
 from craftground.exceptions import (
-    ConnectionTimeoutError,
     InvalidPortError,
     PortInUseError,
 )

40-62: The @patch("socket.socket") decorator is unnecessary.

The mock_socket_class parameter is unused because the test manually assigns ipc.sock = mock_sock. Either remove the decorator or use the patched socket class to create mock_sock.

♻️ Option 1: Remove the decorator
-    `@patch`("socket.socket")
-    def test_send_action(self, mock_socket_class):
+    def test_send_action(self):
         """Test sending action through IPC."""
         logger = Mock(spec=CsvLogger)
         initial_env = Mock(spec=InitialEnvironmentMessage)
♻️ Option 2: Use the patched class
     `@patch`("socket.socket")
     def test_send_action(self, mock_socket_class):
         """Test sending action through IPC."""
         logger = Mock(spec=CsvLogger)
         initial_env = Mock(spec=InitialEnvironmentMessage)
         ipc = SocketIPC(logger, initial_env, port=8000, find_free_port=False)

-        # Mock socket
-        mock_sock = MagicMock()
-        ipc.sock = mock_sock
+        # Use the patched socket class
+        mock_sock = mock_socket_class.return_value
+        ipc.sock = mock_sock

64-83: Same issue: unused mock_socket_class parameter.

Apply the same fix as suggested for test_send_action.

Comment on lines +77 to +80
if port > MAX_PORT:
raise PortInUseError(
f"Could not find available port starting from {port}"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect port value in error message.

When the error is raised, port has already been incremented past MAX_PORT, making the message misleading. The original starting port should be preserved.

Proposed fix
     def check_port(self, port: int) -> int:
+        original_port = port
         # ... validation code ...
         if os.name == "nt":
             while True:
                 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                     if s.connect_ex(("127.0.0.1", port)) == 0:
                         if self.find_free_port:
                             print(...)
                             port += 1
                             if port > MAX_PORT:
                                 raise PortInUseError(
-                                    f"Could not find available port starting from {port}"
+                                    f"Could not find available port starting from {original_port}"
                                 )
🧰 Tools
🪛 Ruff (0.14.13)

78-80: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In `@src/craftground/environment/socket_ipc.py` around lines 77 - 80, The error
message uses the incremented port value instead of the original starting port;
capture the initial starting port (e.g., start_port = port) before the loop that
increments port and then, when raising PortInUseError, use that preserved
start_port in the message (keep the PortInUseError raise site and MAX_PORT check
unchanged but swap the f-string to reference start_port).

Comment on lines +98 to +101
if port > MAX_PORT:
raise PortInUseError(
f"Could not find available port starting from {port}"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same error message issue with incremented port.

Similar to the Windows branch, port has already been incremented past MAX_PORT when this error is raised, making the "starting from" message inaccurate.

🧰 Tools
🪛 Ruff (0.14.13)

99-101: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In `@src/craftground/environment/socket_ipc.py` around lines 98 - 101, The
PortInUseError message is using the already-incremented port value, making
"starting from {port}" inaccurate; change the check in the loop that raises
PortInUseError (where MAX_PORT and port are referenced) to report the original
starting port (store the initial port in a variable like start_port before
incrementing) and use that start_port in the PortInUseError message so it
matches the actual requested start value (reference the port variable, MAX_PORT,
and PortInUseError in your fix).

Comment on lines +281 to +284
if next_output > MAX_CONNECTION_RETRIES:
raise ConnectionTimeoutError(
f"Server not started within {MAX_CONNECTION_RETRIES} seconds"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing exception chaining and premature timeout.

  1. Per static analysis (B904), use raise ... from to preserve the exception chain.
  2. The next_output > MAX_CONNECTION_RETRIES check may trigger before the main wait_time < MAX_CONNECTION_RETRIES condition, causing premature timeout at 64 seconds (if MAX_CONNECTION_RETRIES is 60) rather than the intended retry count.
Proposed fix for exception chaining
             except (ConnectionRefusedError, FileNotFoundError) as e:
                 if wait_time == next_output:
                     print(...)
                     next_output *= 2
                     if next_output > MAX_CONNECTION_RETRIES:
                         raise ConnectionTimeoutError(
                             f"Server not started within {MAX_CONNECTION_RETRIES} seconds"
-                        )
+                        ) from e

Consider whether the next_output > MAX_CONNECTION_RETRIES check is intentional or if it should be removed to let the main loop condition handle timeouts.

🧰 Tools
🪛 Ruff (0.14.13)

282-284: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)


282-284: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In `@src/craftground/environment/socket_ipc.py` around lines 281 - 284, The
premature timeout comes from the extra next_output > MAX_CONNECTION_RETRIES
check and the raised ConnectionTimeoutError lacks exception chaining; update the
logic in socket_ipc.py (look for next_output, wait_time, MAX_CONNECTION_RETRIES,
and ConnectionTimeoutError) by removing the redundant next_output >
MAX_CONNECTION_RETRIES branch so the loop relies solely on wait_time <
MAX_CONNECTION_RETRIES for termination, and when you do raise
ConnectionTimeoutError include the original exception with "raise
ConnectionTimeoutError(...) from err" (capture the original exception variable
where the wait/connection error occurred) to preserve the exception chain.

Copilot AI 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.

Pull request overview

This PR refactors the CraftGround codebase by introducing better error handling, extracting constants, improving code organization, and adding validation. The refactoring focuses on making the code more maintainable and testable while preserving functionality.

Changes:

  • Introduced a new exceptions module with custom exception classes for better error categorization
  • Extracted magic numbers into a constants module for better maintainability
  • Added validation for image sizes and port numbers in configuration
  • Refactored process termination logic into separate platform-specific methods
  • Simplified action_space conversion using loops instead of repetitive assignments
  • Added comprehensive docstrings to public methods
  • Added new unit tests for IPC and environment lifecycle

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/craftground/exceptions.py New file defining custom exception hierarchy for CraftGround errors
src/craftground/constants.py New file centralizing magic numbers and configuration constants
src/craftground/initial_environment_config.py Added validation for image dimensions using new constants and exceptions
src/craftground/environment/socket_ipc.py Enhanced port validation, improved error handling, and added comprehensive docstrings
src/craftground/environment/environment.py Refactored process termination into platform-specific methods with better error handling
src/craftground/environment/action_space.py Simplified action conversion logic using loops and .get() with defaults
tests/python/unit/test_ipc_mock.py New test file for IPC functionality with mocking
tests/python/unit/test_environment_lifecycle.py New test file for environment creation and configuration validation
src/craftground/MinecraftEnv/.gitignore Added minecraft_sources to ignore list
.gitignore Added CODE_REVIEW.md to ignore list
Comments suppressed due to low confidence (1)

src/craftground/environment/socket_ipc.py:86

  • The Windows port checking logic uses an infinite loop (while True) that only exits when a port is available or an exception is raised. If find_free_port is True and all ports from the starting port to MAX_PORT are in use, the exception is raised correctly. However, the loop could be made clearer by adding an explicit condition like 'while port <= MAX_PORT' to make the termination condition more obvious.
            while True:
                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                    if s.connect_ex(("127.0.0.1", port)) == 0:  # The port is in use
                        if self.find_free_port:
                            print(
                                f"[Warning]: Port {port} is already in use. Trying another port."
                            )
                            port += 1
                            if port > MAX_PORT:
                                raise PortInUseError(
                                    f"Could not find available port starting from {port}"
                                )
                        else:
                            raise PortInUseError(
                                f"Port {port} is already in use. Please choose another port."
                            )
                    else:
                        return port

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +96 to +102
setattr(action_space, action_name, action_v2.get(action_name, False))

# Hotbar actions (1-9)
for i in range(1, 10):
hotbar_key = f"hotbar.{i}"
hotbar_attr = f"hotbar_{i}"
setattr(action_space, hotbar_attr, action_v2.get(hotbar_key, False))

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

The refactored code changes the behavior when action keys are missing from the input dictionary. The original code would raise a KeyError if a required action key was missing, while the new code silently defaults to False for missing keys. This could hide bugs where actions are incorrectly specified. Consider whether this is intentional or if the original error-raising behavior should be preserved for required keys.

Suggested change
setattr(action_space, action_name, action_v2.get(action_name, False))
# Hotbar actions (1-9)
for i in range(1, 10):
hotbar_key = f"hotbar.{i}"
hotbar_attr = f"hotbar_{i}"
setattr(action_space, hotbar_attr, action_v2.get(hotbar_key, False))
setattr(action_space, action_name, action_v2[action_name])
# Hotbar actions (1-9)
for i in range(1, 10):
hotbar_key = f"hotbar.{i}"
hotbar_attr = f"hotbar_{i}"
setattr(action_space, hotbar_attr, action_v2[hotbar_key])

Copilot uses AI. Check for mistakes.
Comment on lines +106 to 107
raise PortInUseError(
f"Socket file {socket_path} already exists. Please choose another port."

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

The existing test in test_socket_ipc.py (line 39) expects FileExistsError but the code now raises PortInUseError. This is a breaking change that makes the existing test fail. The test needs to be updated to expect PortInUseError instead of FileExistsError.

Copilot uses AI. Check for mistakes.

if port > MAX_PORT:
raise PortInUseError(
f"Could not find available port starting from {port}"

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

The error message is similar to line 79 - when port exceeds MAX_PORT, the message will say "Could not find available port starting from {port}" where port is beyond the valid range. Consider capturing the original port value or adjusting the message to be more accurate.

Copilot uses AI. Check for mistakes.
ConnectionTimeoutError: If connection times out
RuntimeError: If server process fails to start
"""
wait_time = 0

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

The variable 'wait_time' is initialized to 0 and counts the number of retries, but the constant MAX_CONNECTION_RETRIES is set to 1024 seconds. The comment on line 258 describes next_output as tracking doubling intervals (1, 2, 4, 8... seconds), which suggests time-based progression, but wait_time is actually counting iterations. Consider renaming wait_time to retry_count or elapsed_seconds for clarity, or adjust the logic to match the semantics.

Copilot uses AI. Check for mistakes.

# Connection settings
MAX_CONNECTION_RETRIES = 1024
CONNECTION_RETRY_INTERVAL = 1.0

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

CONNECTION_RETRY_INTERVAL is defined but never used in the codebase. The retry logic in socket_ipc.py uses time.sleep(1) directly instead of this constant. Consider either using this constant in the sleep call (line 286 of socket_ipc.py) or removing it if it's not needed.

Suggested change
CONNECTION_RETRY_INTERVAL = 1.0

Copilot uses AI. Check for mistakes.
port += 1
if port > MAX_PORT:
raise PortInUseError(
f"Could not find available port starting from {port}"

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

The error message is misleading. When the port reaches MAX_PORT, the error message will say "Could not find available port starting from {port}" where port is already MAX_PORT + 1 (beyond the valid range). Consider capturing the original port value before incrementing, or adjust the message to reflect that all ports in the valid range have been exhausted.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +45
def test_check_port_validation(self):
"""Test port number validation."""
logger = Mock(spec=CsvLogger)
initial_env = Mock(spec=InitialEnvironmentMessage)

# Invalid port (too low)
with pytest.raises(InvalidPortError):
SocketIPC(logger, initial_env, port=0, find_free_port=False)

# Invalid port (too high)
with pytest.raises(InvalidPortError):
SocketIPC(logger, initial_env, port=70000, find_free_port=False)

# Invalid type
with pytest.raises(TypeError):
SocketIPC(logger, initial_env, port="8000", find_free_port=False)

@patch("socket.socket")
def test_send_action(self, mock_socket_class):
"""Test sending action through IPC."""
logger = Mock(spec=CsvLogger)
initial_env = Mock(spec=InitialEnvironmentMessage)
ipc = SocketIPC(logger, initial_env, port=8000, find_free_port=False)

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

The SocketIPC constructor calls remove_orphan_java_processes() which iterates over processes and may perform system calls. In unit tests, this should be mocked to avoid side effects and dependencies on the system state. Consider adding @patch('craftground.environment.socket_ipc.SocketIPC.remove_orphan_java_processes') to these test methods.

Copilot uses AI. Check for mistakes.
import pytest

from craftground.buffered_socket import BufferedSocket
from craftground.csv_logger import CsvLogger, LogBackend

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

LogBackend is imported but never used in this test file. Consider removing this unused import to keep the code clean.

Suggested change
from craftground.csv_logger import CsvLogger, LogBackend
from craftground.csv_logger import CsvLogger

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +17
from craftground.buffered_socket import BufferedSocket
from craftground.csv_logger import CsvLogger, LogBackend
from craftground.environment.socket_ipc import SocketIPC
from craftground.exceptions import (
ConnectionTimeoutError,
InvalidPortError,
PortInUseError,
)
from craftground.proto.action_space_pb2 import ActionSpaceMessageV2
from craftground.proto.initial_environment_pb2 import InitialEnvironmentMessage
from craftground.proto.observation_space_pb2 import ObservationSpaceMessage

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

Several imports (BufferedSocket, ConnectionTimeoutError, ObservationSpaceMessage) are imported but never used in this test file. Consider removing these unused imports to keep the code clean.

Suggested change
from craftground.buffered_socket import BufferedSocket
from craftground.csv_logger import CsvLogger, LogBackend
from craftground.environment.socket_ipc import SocketIPC
from craftground.exceptions import (
ConnectionTimeoutError,
InvalidPortError,
PortInUseError,
)
from craftground.proto.action_space_pb2 import ActionSpaceMessageV2
from craftground.proto.initial_environment_pb2 import InitialEnvironmentMessage
from craftground.proto.observation_space_pb2 import ObservationSpaceMessage
from craftground.csv_logger import CsvLogger, LogBackend
from craftground.environment.socket_ipc import SocketIPC
from craftground.exceptions import (
InvalidPortError,
PortInUseError,
)
from craftground.proto.action_space_pb2 import ActionSpaceMessageV2
from craftground.proto.initial_environment_pb2 import InitialEnvironmentMessage

Copilot uses AI. Check for mistakes.
import threading
from typing import Tuple, Optional, TypedDict, Union, List, Any, Dict
from enum import Enum
from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union

Copilot AI Jan 26, 2026

Copy link

Choose a reason for hiding this comment

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

Import of 'Any' is not used.

Suggested change
from typing import Any, Dict, List, Optional, Tuple, TypedDict, Union
from typing import Dict, List, Optional, Tuple, TypedDict, Union

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants