Skip to content

Dev#508

Merged
hect0x7 merged 8 commits intomasterfrom
dev
Jan 8, 2026
Merged

Dev#508
hect0x7 merged 8 commits intomasterfrom
dev

Conversation

@hect0x7
Copy link
Owner

@hect0x7 hect0x7 commented Jan 8, 2026

Summary by CodeRabbit

  • New Features

    • Redesigned API domain update flow with more reliable multi-source fetch and fallback handling.
    • Added robust version comparison logic to better determine upgrades.
  • Bug Fixes

    • Improved API response validation to catch empty/error payloads earlier.
    • Fixed handling of album/photo properties in path formatting when values are falsy.
  • Chores

    • Updated application version to 2.0.13

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Warning

Rate limit exceeded

@hect0x7 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 6 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 8df9bb2 and 2018e41.

📒 Files selected for processing (2)
  • src/jmcomic/__init__.py
  • src/jmcomic/jm_client_impl.py
📝 Walkthrough
📝 Walkthrough
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The pull request title 'Dev' is vague and generic, providing no meaningful information about the specific changes in the changeset. Replace the title with a specific, descriptive summary of the main change, such as 'Refactor API domain update mechanism with improved caching and version comparison' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @src/jmcomic/jm_client_impl.py:
- Around line 995-998: The function update_old_api_domain uses the Python 3.9+
annotation list[str], which can break compatibility; change the parameter type
hint to use typing.List[str] (i.e., List[str]) instead, referencing the existing
import at the top, so update the signature of update_old_api_domain(self,
new_server_list: List[str]) while leaving the body and the check against
JmModuleConfig.DOMAIN_API_LIST unchanged.
- Around line 773-780: Wrap the call to JmcomicText.compare_versions that
compares setting_ver (from resp.model_data.jm3_version) and
JmMagicConstants.APP_VERSION in a try/except to catch parsing errors (e.g.,
ValueError); on exception log the failure with jm_log('api.setting', ...)
including the bad setting_ver and exception details, and skip changing
JmMagicConstants.APP_VERSION; keep the existing branch gated by
JmModuleConfig.FLAG_USE_VERSION_NEWER_IF_BEHIND and only perform the assignment
when compare_versions returns 1 without error.

In @src/jmcomic/jm_toolkit.py:
- Around line 417-432: The compare_versions method currently converts
dot-separated parts to int without validation, which will raise on malformed
parts; update compare_versions to validate and normalize each segment before int
conversion: for v1 and v2 split on '.', for each segment extract the leading
numeric portion (e.g., via a regex like r'^\d+') and convert that to int (treat
missing numeric portion as 0 or raise a controlled ValueError), wrap the
conversion in try/except to raise a clear error message that includes the
original inputs when conversion fails, and continue using parts1/parts2 after
padding as before for the comparison logic.
🧹 Nitpick comments (1)
src/jmcomic/jm_client_impl.py (1)

999-1032: Domain fetch logic is robust but consider adding timeout.

The fetch_latest_api_domain_for_module() method implements a solid caching and retry strategy with double-checked locking. The approach of setting DOMAIN_API_UPDATED_LIST = [] as a "done" signal when no updates are found is clever.

However, consider adding a timeout to req_api_domain_server() to prevent indefinite hangs if a domain server is unresponsive.

⏱️ Suggested improvement: Add timeout to domain server requests

In the req_api_domain_server method (line 978), add a timeout parameter:

def req_api_domain_server(self, url):
    resp = self.postman.get(url, timeout=10)  # Add timeout
    text: str = resp.text
    # ... rest of the method

This prevents the application from hanging if a domain server is slow or unresponsive.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ed1cac and 73fe17e.

📒 Files selected for processing (5)
  • src/jmcomic/jm_client_impl.py
  • src/jmcomic/jm_client_interface.py
  • src/jmcomic/jm_config.py
  • src/jmcomic/jm_option.py
  • src/jmcomic/jm_toolkit.py
🧰 Additional context used
🪛 Ruff (0.14.10)
src/jmcomic/jm_client_interface.py

128-128: ExceptionTool may be undefined, or defined from star imports

(F405)


131-131: Any may be undefined, or defined from star imports

(F405)


138-138: AdvancedDict may be undefined, or defined from star imports

(F405)

src/jmcomic/jm_client_impl.py

776-776: JmModuleConfig may be undefined, or defined from star imports

(F405)


777-777: JmcomicText may be undefined, or defined from star imports

(F405)


777-777: JmMagicConstants may be undefined, or defined from star imports

(F405)


924-924: Comment contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF003)


996-996: JmModuleConfig may be undefined, or defined from star imports

(F405)


1000-1000: JmModuleConfig may be undefined, or defined from star imports

(F405)


1001-1001: JmModuleConfig may be undefined, or defined from star imports

(F405)


1005-1005: JmModuleConfig may be undefined, or defined from star imports

(F405)


1006-1006: JmModuleConfig may be undefined, or defined from star imports

(F405)


1019-1019: JmModuleConfig may be undefined, or defined from star imports

(F405)


1020-1020: Consider moving this statement to an else block

(TRY300)


1021-1021: Do not catch blind exception: Exception

(BLE001)


1022-1022: jm_log may be undefined, or defined from star imports

(F405)


1023-1023: String contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF001)


1024-1024: String contains ambiguous (FULLWIDTH COLON). Did you mean : (COLON)?

(RUF001)


1028-1028: Comment contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF003)


1029-1029: Comment contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF003)


1031-1031: JmModuleConfig may be undefined, or defined from star imports

(F405)


1032-1032: JmModuleConfig may be undefined, or defined from star imports

(F405)

🔇 Additional comments (8)
src/jmcomic/jm_option.py (1)

133-136: LGTM! Explicit None checks improve robustness.

The change from truthiness checks to explicit is not None comparisons is safer and more explicit. This ensures that album and photo objects are processed even if they implement __bool__ or __len__ methods that return falsy values, preventing potential edge-case bugs.

src/jmcomic/jm_client_interface.py (2)

125-128: LGTM! Good defensive validation for API responses.

The new require_have_data() method adds proper validation for API responses where data is an empty list but an errorMsg exists. This prevents downstream errors when attempting to process empty data and provides clear error messages.


133-133: LGTM! Validation properly integrated into response properties.

The require_have_data() calls are correctly placed after require_success() in both res_data and model_data properties, ensuring data validation happens before decoding or processing.

Also applies to: 140-140

src/jmcomic/jm_config.py (2)

80-80: LGTM! Version bump to 2.0.13.

The APP_VERSION update aligns with the new version handling logic introduced in this PR.


137-137: LGTM! Improved domain update state management.

The new DOMAIN_API_UPDATED_LIST attribute provides better state management than the previous flag-based approach. It can distinguish between three states: not fetched yet (None), fetched but no updates ([]), and updated domains (list of domains).

src/jmcomic/jm_client_impl.py (3)

924-924: LGTM! API behavior change properly documented.

The comment correctly indicates that the latest API no longer returns empty data arrays for missing albums, so the check is no longer needed. This aligns with the new require_have_data() validation in JmApiResp.


969-970: LGTM! Improved separation of concerns for domain updates.

The refactored domain update flow properly separates fetching the latest domains from applying the update. The fetch_latest_api_domain_for_module() handles caching and retrieval, while update_old_api_domain() conditionally applies the update based on whether the client is using default domains.


1009-1026: LGTM! Resilient error handling with proper fallback.

The domain fetching logic correctly iterates through all available domain server URLs, logging failures and continuing to the next URL. The broad exception catching is appropriate here for resilience, ensuring one failed URL doesn't prevent trying alternatives.

Comment on lines +773 to 780
setting_ver = str(resp.model_data.jm3_version)
# 禁漫接口的版本 > jmcomic库内置版本
if setting_ver > JmMagicConstants.APP_VERSION and JmModuleConfig.FLAG_USE_VERSION_NEWER_IF_BEHIND:
if (
JmModuleConfig.FLAG_USE_VERSION_NEWER_IF_BEHIND
and JmcomicText.compare_versions(setting_ver, JmMagicConstants.APP_VERSION) == 1
):
jm_log('api.setting', f'change APP_VERSION from [{JmMagicConstants.APP_VERSION}] to [{setting_ver}]')
JmMagicConstants.APP_VERSION = setting_ver
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify jm3_version field usage and version comparison logic
rg -n "jm3_version|\"version\"" --type=py -C 3

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 538


🏁 Script executed:

#!/bin/bash
# Find and examine the compare_versions function implementation
rg -n "def compare_versions" --type=py -A 15

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 1020


🏁 Script executed:

#!/bin/bash
# Check for any existing error handling around version comparisons
rg -n "try.*version|except.*version" --type=py -B 2 -A 5

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 56


🏁 Script executed:

#!/bin/bash
# Check the implementation details and see if there's any validation
# Let's see more context around compare_versions
rg -n "def compare_versions" --type=py -A 20

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 1313


🏁 Script executed:

#!/bin/bash
# Search for the model/API response definition to see how jm3_version is handled
rg -n "jm3_version|setting.*resp|api.*response" --type=py | head -20

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 358


🏁 Script executed:

#!/bin/bash
# Check if there's any try-catch around the API call itself
rg -n "req_api|resp\s*=.*setting" --type=py -B 2 -A 10 | grep -A 15 "/setting"

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 1189


🏁 Script executed:

#!/bin/bash
# Check what APP_VERSION looks like and how it's defined
rg -n "APP_VERSION\s*=" --type=py -B 2 -A 2 | head -30

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 715


🏁 Script executed:

#!/bin/bash
# Look for the API response model definition (likely in a models or api file)
fd -e py | xargs grep -l "jm3_version\|model_data" | head -5

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 164


🏁 Script executed:

#!/bin/bash
# Check the context around the req_api call and any error handling in that method
rg -n "def req_api" --type=py -A 20 | head -40

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 2749


🏁 Script executed:

#!/bin/bash
# Find JmApiResp class definition and model_data property
rg -n "class JmApiResp|model_data" --type=py -B 2 -A 10 | head -60

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 3887


🏁 Script executed:

#!/bin/bash
# Check if there's any data validation or error handling at the calling site
sed -n '765,785p' src/jmcomic/jm_client_impl.py

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 728


🏁 Script executed:

#!/bin/bash
# Look for any usage of compare_versions to see if error handling is done elsewhere
rg -n "compare_versions" --type=py -B 3 -A 3

Repository: hect0x7/JMComic-Crawler-Python

Length of output: 1130


Add error handling for version comparison failures.

The compare_versions() call at line 777 lacks defensive error handling. If the API response contains a malformed jm3_version field (non-numeric segments), compare_versions() will raise a ValueError when attempting to parse version parts. Wrap the comparison in a try-catch block to handle version comparison failures gracefully and prevent crashes on unexpected API data.

🧰 Tools
🪛 Ruff (0.14.10)

776-776: JmModuleConfig may be undefined, or defined from star imports

(F405)


777-777: JmcomicText may be undefined, or defined from star imports

(F405)


777-777: JmMagicConstants may be undefined, or defined from star imports

(F405)


779-779: jm_log may be undefined, or defined from star imports

(F405)


779-779: JmMagicConstants may be undefined, or defined from star imports

(F405)


780-780: JmMagicConstants may be undefined, or defined from star imports

(F405)

🤖 Prompt for AI Agents
In @src/jmcomic/jm_client_impl.py around lines 773 - 780, Wrap the call to
JmcomicText.compare_versions that compares setting_ver (from
resp.model_data.jm3_version) and JmMagicConstants.APP_VERSION in a try/except to
catch parsing errors (e.g., ValueError); on exception log the failure with
jm_log('api.setting', ...) including the bad setting_ver and exception details,
and skip changing JmMagicConstants.APP_VERSION; keep the existing branch gated
by JmModuleConfig.FLAG_USE_VERSION_NEWER_IF_BEHIND and only perform the
assignment when compare_versions returns 1 without error.

Comment on lines +417 to +432
@classmethod
def compare_versions(cls, v1: str, v2: str) -> int:
parts1 = list(map(int, v1.split(".")))
parts2 = list(map(int, v2.split(".")))

# 补齐长度
length = max(len(parts1), len(parts2))
parts1 += [0] * (length - len(parts1))
parts2 += [0] * (length - len(parts2))

if parts1 > parts2:
return 1 # v1 大
elif parts1 < parts2:
return -1 # v2 大
else:
return 0 # 相等
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling for invalid version strings.

The compare_versions method lacks input validation and error handling. If version strings contain non-numeric parts (e.g., "1.0.0-alpha") or are malformed, int() conversion will raise a ValueError, potentially crashing the application.

Consider adding defensive error handling:

🛡️ Proposed fix with error handling
 @classmethod
 def compare_versions(cls, v1: str, v2: str) -> int:
+    try:
         parts1 = list(map(int, v1.split(".")))
         parts2 = list(map(int, v2.split(".")))
+    except (ValueError, AttributeError) as e:
+        jm_log('version.compare.error', f'Invalid version format: v1={v1}, v2={v2}, error={e}')
+        return 0  # Treat as equal if comparison fails
 
     # 补齐长度
     length = max(len(parts1), len(parts2))
     parts1 += [0] * (length - len(parts1))
     parts2 += [0] * (length - len(parts2))
 
     if parts1 > parts2:
         return 1  # v1 大
     elif parts1 < parts2:
         return -1  # v2 大
     else:
         return 0  # 相等
🤖 Prompt for AI Agents
In @src/jmcomic/jm_toolkit.py around lines 417 - 432, The compare_versions
method currently converts dot-separated parts to int without validation, which
will raise on malformed parts; update compare_versions to validate and normalize
each segment before int conversion: for v1 and v2 split on '.', for each segment
extract the leading numeric portion (e.g., via a regex like r'^\d+') and convert
that to int (treat missing numeric portion as 0 or raise a controlled
ValueError), wrap the conversion in try/except to raise a clear error message
that includes the original inputs when conversion fails, and continue using
parts1/parts2 after padding as before for the comparison logic.

@hect0x7 hect0x7 merged commit 0651403 into master Jan 8, 2026
@hect0x7 hect0x7 deleted the dev branch January 8, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant