Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(oocana): use dict mapping for compression_suffix instead of if-elif chain by leavesster · Pull Request #457 · oomol/oocana-python · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions oocana/oocana/serialization.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,28 +69,28 @@ def compression_options(context: 'Context') -> CompressionOptions | None:
print(f"An unexpected error occurred while reading compression options: {e}. Returning None.")
return None

# Mapping from compression method to file suffix
COMPRESSION_SUFFIXES = {
"zip": ".zip",
"gzip": ".gz",
"bz2": ".bz2",
"zstd": ".zst",
"xz": ".xz",
"tar": ".tar",
}
Comment on lines +72 to +80

CopilotAIJan 31, 2026

Copy link

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.

def compression_suffix(context: 'Context') -> str:
"""
Get the file suffix based on the compression method.
If no compression is specified, return an empty string.
If no compression is specified, return ".pkl" (pickle format).
"""
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

method = compression.get("method")
if method is None:
Comment on lines +92 to +93

CopilotAIJan 31, 2026

Copy link

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")
ifmethodisNone:
# Ensure we have a dictionary before accessing keys. Malformed or unexpected
# JSON in the options file may result in a non-dict value here.
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
# Guard against non-string (and thus potentially unhashable) methods.
ifnotisinstance(method, str):

Copilot uses AI. Check for mistakes.
return ".pkl"

return COMPRESSION_SUFFIXES.get(method, ".pkl")
Comment on lines 87 to +96

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

防止压缩配置为非 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)
ifcompressionisNoneorcompression["method"] isNone:
ifcompressionisNone:
return".pkl"
method=compression["method"]
ifmethod=="zip":
return".zip"
elifmethod=="gzip":
return".gz"
elifmethod=="bz2":
return".bz2"
elifmethod=="zstd":
return".zst"
elifmethod=="xz":
return".xz"
elifmethod=="tar":
return".tar"
else:
return".pkl"# Default case if method is not recognized
\ Nonewlineatendoffile
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_SUFFIXES.get(method, ".pkl")
compression=compression_options(context)
ifnotisinstance(compression, dict):
return".pkl"
method=compression.get("method")
ifmethodisNone:
return".pkl"
returnCOMPRESSION_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") 返回结果。

164 changes: 164 additions & 0 deletions oocana/tests/test_serialization.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
import unittest
from unittest.mock import MagicMock, patch

CopilotAIJan 31, 2026

Copy link

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
fromunittest.mockimportMagicMock, patch
fromunittest.mockimportMagicMock

Copilot uses AI. Check for mistakes.
import tempfile
import os
import json


class TestCompressionSuffix(unittest.TestCase):
"""Test cases for compression_suffix function."""

def setUp(self):
# Create a temporary directory for the mock context
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
# Clean up temporary files
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_suffix_no_options(self):
"""Test that .pkl is returned when no compression options exist."""
from oocana.oocana.serialization import compression_suffix

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

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_bz2(self):
"""Test compression suffix for bz2 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": "bz2"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".bz2")

def test_compression_suffix_zstd(self):
"""Test compression suffix for zstd 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": "zstd"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".zst")

def test_compression_suffix_xz(self):
"""Test compression suffix for xz 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": "xz"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".xz")

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_null_method(self):
"""Test that .pkl is returned when method is null."""
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": None}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_missing_method_key(self):
"""Test that .pkl is returned when method key is missing (no KeyError)."""
from oocana.oocana.serialization import compression_suffix, COMPRESSION_OPTIONS_FILE

# Write a dict without the 'method' key
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump({"other_key": "value"}, f)

# Should not raise KeyError
result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")

def test_compression_suffix_unknown_method(self):
"""Test that .pkl is returned for unknown compression 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": "unknown_method"}, f)

result = compression_suffix(self.mock_context)
self.assertEqual(result, ".pkl")


class TestCompressionOptions(unittest.TestCase):
"""Test cases for compression_options function."""

def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.mock_context = MagicMock()
self.mock_context.pkg_data_dir = self.temp_dir

def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)

def test_compression_options_returns_none_when_no_file(self):
"""Test that None is returned when options file doesn't exist."""
from oocana.oocana.serialization import compression_options

result = compression_options(self.mock_context)
self.assertIsNone(result)

def test_compression_options_returns_dict_when_file_exists(self):
"""Test that options are returned when file exists."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

expected = {"method": "gzip"}
with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
json.dump(expected, f)

result = compression_options(self.mock_context)
self.assertEqual(result, expected)

def test_compression_options_handles_invalid_json(self):
"""Test that None is returned for invalid JSON."""
from oocana.oocana.serialization import compression_options, COMPRESSION_OPTIONS_FILE

with open(os.path.join(self.temp_dir, COMPRESSION_OPTIONS_FILE), "w") as f:
f.write("not valid json")

result = compression_options(self.mock_context)
self.assertIsNone(result)


if __name__ == '__main__':
unittest.main()
Loading