Skip to content

Conversation

@leavesster
Copy link
Contributor

Summary

  • Replace if-elif chain with COMPRESSION_SUFFIXES dictionary mapping
  • Use .get() to safely handle missing keys
  • Add unit tests for compression_suffix() function

Problem

Original code had long if-elif chain with potential KeyError risk:

if compression["method"] == "zip":
    return ".zip"
elif compression["method"] == "gzip":
    return ".gz"
# ... 6 more branches

Solution

COMPRESSION_SUFFIXES = {
    "zip": ".zip", "gzip": ".gz", "bz2": ".bz2",
    "zstd": ".zst", "xz": ".xz", "tar": ".tar",
}

def compression_suffix(context):
    compression = compression_options(context)
    if compression is None:
        return ".pkl"
    return COMPRESSION_SUFFIXES.get(compression.get("method"), ".pkl")

Test Plan

  • Added tests/test_serialization.py with coverage for all compression methods
  • All existing tests pass

Replace if-elif chain with a dictionary mapping for cleaner code.
Also fix potential KeyError by using .get() method.

Changes:
- Add COMPRESSION_SUFFIXES constant mapping method to suffix
- Use .get() to safely access 'method' key
- Add comprehensive unit tests for compression_suffix
Copilot AI review requested due to automatic review settings January 31, 2026 09:27
@coderabbitai
Copy link

coderabbitai bot commented Jan 31, 2026

总体说明

本次变更通过引入 COMPRESSION_SUFFIXES 映射表,将压缩后缀逻辑从多个条件分支重构为单一映射查询机制。同时新增了一个全面的测试模块,用于验证压缩后缀和压缩选项在各种场景下的行为。

变更内容

文件群组 / 文件路径 变更摘要
核心序列化重构
oocana/oocana/serialization.py
引入 COMPRESSION_SUFFIXES 常量映射压缩方法与文件后缀;重构 compression_suffix 函数,将多个条件分支替换为映射查询,默认返回 ".pkl";移除显式的按方法分发逻辑。
测试套件新增
oocana/tests/test_serialization.py
新增 164 行测试代码,覆盖 compression_suffix 函数在多种压缩方法(zip、gzip、bz2、zstd、xz、tar)及边界情况下的行为;测试 compression_options 函数处理缺失、有效和无效 JSON 文件的场景;使用临时目录和 mock 上下文完成测试隔离。

代码审查工作量估算

🎯 3 (中等复杂度) | ⏱️ ~20 分钟

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed PR description is well-related to the changeset, providing clear problem statement, solution details with code examples, and test plan that align with the actual changes made.
Title check ✅ Passed The pull request title follows the required format with type(scope): subject pattern, clearly describing the main refactoring change from if-elif chain to dict mapping.

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


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

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors compression_suffix() to use a dictionary mapping for compression method → file suffix (instead of an if/elif chain) and adds unit tests to validate behavior across supported and edge-case methods.

Changes:

  • Introduced COMPRESSION_SUFFIXES mapping and updated compression_suffix() to use .get()-based lookup with defaults.
  • Updated compression_suffix() docstring to match actual default behavior (.pkl).
  • Added unit tests covering supported methods and fallback behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
oocana/oocana/serialization.py Replaces if/elif suffix selection with a mapping constant and simplified lookup logic.
oocana/tests/test_serialization.py Adds tests for compression_suffix() and compression_options() behavior.

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

@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

patch is imported but never used in this test module; removing it avoids lint warnings and keeps the test file tidy.

Suggested change
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +93
method = compression.get("method")
if method is None:
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

COMPRESSION_SUFFIXES.get(method, ...) assumes method is a hashable key. If the options file contains a non-string (e.g., a list/dict from valid-but-unexpected JSON), this will raise TypeError and crash, whereas the previous if/elif chain would safely fall back. Consider guarding with isinstance(method, str) (and/or isinstance(compression, dict)) before doing the dict lookup, otherwise return the default suffix.

Suggested change
method = compression.get("method")
if method is None:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
if not isinstance(compression, dict):
return ".pkl"
method = compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
if not isinstance(method, str):

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +80
# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

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

The compression methods are now duplicated in three places (SUPPORTED_COMPRESSION_METHODS, the CompressionOptions Literal, and COMPRESSION_SUFFIXES). This can drift over time (e.g., a method added to SUPPORTED_COMPRESSION_METHODS but missing here would silently fall back to .pkl). Consider making one source of truth (e.g., derive SUPPORTED_COMPRESSION_METHODS from COMPRESSION_SUFFIXES.keys()), and keep the type Literal in sync with that constant.

Copilot uses AI. Check for mistakes.
Copy link

@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

🤖 Fix all issues with AI agents
In `@oocana/oocana/serialization.py`:
- Around line 87-96: compression_options 返回的 compression 可能不是 dict,从而在
compression.get("method") 处抛出 AttributeError;在函数/片段中(使用
compression_options、compression、method、COMPRESSION_SUFFIXES 的地方)先对 compression
做类型保护(例如 isinstance(compression, dict) 或
collections.abc.Mapping),若不是合适的映射类型则直接返回默认后缀 ".pkl";若是映射再安全读取 method 并用
COMPRESSION_SUFFIXES.get(method, ".pkl") 返回结果。
🧹 Nitpick comments (2)
oocana/oocana/serialization.py (1)

72-80: 避免与 SUPPORTED_COMPRESSION_METHODS 重复来源,防止漂移
目前方法列表在 SUPPORTED_COMPRESSION_METHODSCOMPRESSION_SUFFIXES 中重复维护,后续新增/删除方法容易不同步。建议以 COMPRESSION_SUFFIXES 作为单一来源生成列表。若该常量希望对外公开,也可同步加入 __all__

🔧 参考改法(统一来源)
-SUPPORTED_COMPRESSION_METHODS = ["zip", "gzip", "bz2", "zstd", "xz", "tar"]
+# Mapping from compression method to file suffix
+COMPRESSION_SUFFIXES = {
+    "zip": ".zip",
+    "gzip": ".gz",
+    "bz2": ".bz2",
+    "zstd": ".zst",
+    "xz": ".xz",
+    "tar": ".tar",
+}
+SUPPORTED_COMPRESSION_METHODS = list(COMPRESSION_SUFFIXES.keys())
oocana/tests/test_serialization.py (1)

29-119: 建议参数化这些重复的后缀测试
zip/gzip/bz2/zstd/xz/tar 的测试结构完全一致,后续维护成本偏高。可以用 subTest + 参数表合并为一个测试。

♻️ 参考改法(参数化)
-    def test_compression_suffix_zip(self):
-        """Test compression suffix for zip method."""
-        from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE
-
-        with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
-            json.dump({"method": "zip"}, f)
-
-        result = compression_suffix(self.mock_context)
-        self.assertEqual(result, ".zip")
-
-    def test_compression_suffix_gzip(self):
-        """Test compression suffix for gzip method."""
-        from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE
-
-        with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
-            json.dump({"method": "gzip"}, f)
-
-        result = compression_suffix(self.mock_context)
-        self.assertEqual(result, ".gz")
-
-    ...
-
-    def test_compression_suffix_tar(self):
-        """Test compression suffix for tar method."""
-        from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE
-
-        with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
-            json.dump({"method": "tar"}, f)
-
-        result = compression_suffix(self.mock_context)
-        self.assertEqual(result, ".tar")
+    def test_compression_suffix_methods(self):
+        """Test compression suffix for supported methods."""
+        from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE
+
+        cases = {
+            "zip": ".zip",
+            "gzip": ".gz",
+            "bz2": ".bz2",
+            "zstd": ".zst",
+            "xz": ".xz",
+            "tar": ".tar",
+        }
+        for method, expected in cases.items():
+            with self.subTest(method=method):
+                with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
+                    json.dump({"method": method}, f)
+                self.assertEqual(compression_suffix(self.mock_context), expected)

Comment on lines 87 to 96
compression = compression_options(context)

if compression is None or compression["method"] is None:
if compression is None:
return ".pkl"

method = compression["method"]
if method == "zip":
return ".zip"
elif method == "gzip":
return ".gz"
elif method == "bz2":
return ".bz2"
elif method == "zstd":
return ".zst"
elif method == "xz":
return ".xz"
elif method == "tar":
return ".tar"
else:
return ".pkl" # Default case if method is not recognized No newline at end of file

method = compression.get("method")
if method is None:
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl") No newline at end of file
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

防止压缩配置为非 dict 时触发 AttributeError
compression_options 读取的是任意 JSON,若文件内容为合法但非 dict(如字符串/列表),compression.get(...) 会直接报错。建议加类型保护并回退到默认后缀。

🛡️ 参考修复
     compression = compression_options(context)

-    if compression is None:
+    if not isinstance(compression, dict):
         return ".pkl"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
compression = compression_options(context)
if compression is None or compression["method"] is None:
if compression is None:
return ".pkl"
method = compression["method"]
if method == "zip":
return ".zip"
elif method == "gzip":
return ".gz"
elif method == "bz2":
return ".bz2"
elif method == "zstd":
return ".zst"
elif method == "xz":
return ".xz"
elif method == "tar":
return ".tar"
else:
return ".pkl" # Default case if method is not recognized
\ No newline at end of file
method = compression.get("method")
if method is None:
return ".pkl"
return COMPRESSION_SUFFIXES.get(method, ".pkl")
compression = compression_options(context)
if not isinstance(compression, dict):
return ".pkl"
method = compression.get("method")
if method is None:
return ".pkl"
return COMPRESSION_SUFFIXES.get(method, ".pkl")
🤖 Prompt for AI Agents
In `@oocana/oocana/serialization.py` around lines 87 - 96, compression_options 返回的
compression 可能不是 dict,从而在 compression.get("method") 处抛出
AttributeError;在函数/片段中(使用
compression_options、compression、method、COMPRESSION_SUFFIXES 的地方)先对 compression
做类型保护(例如 isinstance(compression, dict) 或
collections.abc.Mapping),若不是合适的映射类型则直接返回默认后缀 ".pkl";若是映射再安全读取 method 并用
COMPRESSION_SUFFIXES.get(method, ".pkl") 返回结果。

@leavesster leavesster changed the title fix: use dict mapping for compression_suffix instead of if-elif chain fix(oocana): use dict mapping for compression_suffix instead of if-elif chain Jan 31, 2026
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