Skip to content

dir_rule重构 #426#429

Merged
hect0x7 merged 3 commits intomasterfrom
dev
Apr 22, 2025
Merged

dir_rule重构 #426#429
hect0x7 merged 3 commits intomasterfrom
dev

Conversation

@hect0x7
Copy link
Owner

@hect0x7 hect0x7 commented Apr 22, 2025

Summary by CodeRabbit

  • Refactor
    • Simplified directory naming rules by replacing the previous abstraction with direct parser functions for clearer and more maintainable processing.
    • Updated zip file path computation to use the photo’s album context for more accurate file organization.

@coderabbitai
Copy link

coderabbitai bot commented Apr 22, 2025

Walkthrough

This change refactors the DirRule class in src/jmcomic/jm_option.py by replacing the previous "solver" tuple-based abstraction with a direct parser function approach for directory naming rules. Methods related to rule parsing, application, and splitting are updated to use parser functions instead of solvers. Several new parser methods are introduced (parse_bd_rule, parse_f_string_rule, parse_detail_rule), and method signatures are adjusted to reflect the new design. The logic for applying rules is streamlined by calling parsers directly, and error handling is updated accordingly. Minor updates include renaming internal attributes and adding type checker suppressions. Additionally, in src/jmcomic/jm_plugin.py, the ZipPlugin.invoke method was modified to pass the photo’s album context when computing zip paths instead of None. The module version string in src/jmcomic/__init__.py was also updated.

Changes

File(s) Change Summary
src/jmcomic/jm_option.py Refactored DirRule to replace solver tuples with parser functions; added new parser methods; renamed and updated methods for rule parsing and application; simplified rule splitting; improved error handling and type annotations; minor fixes including fix_windir_name calls and type checker suppression.
src/jmcomic/jm_plugin.py Modified ZipPlugin.invoke to pass photo.from_album instead of None when calling get_zip_path for level 'photo'.
src/jmcomic/init.py Updated module version string from '2.5.36' to '2.5.37'.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DirRule
    participant Album
    participant Photo

    User->>DirRule: decide_image_save_dir(album, photo)
    DirRule->>DirRule: get_rule_parser_list(rule_dsl)
    loop for each (rule, parser)
        DirRule->>parser: parser(album, photo, rule)
        parser-->>DirRule: parsed_path
    end
    DirRule-->>User: image_save_dir
Loading

Possibly related PRs

Poem

A rabbit hopped through code so bright,
Refactored rules—now clear and light!
No more solvers, just parsers here,
Directory names now crystal clear.
With every hop, the logic grew,
Simpler paths for me and you!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
src/jmcomic/jm_option.py (1)

90-100: ⚠️ Potential issue

Album root selection logic is inverted and the wrong value is forwarded

  1. The filter condition skips all rules:

    if rule != 'Bd' or not rule.startswith('A'):
        continue

    rule can never satisfy both parts simultaneously ('Bd' does not start with 'A'), so every rule is skipped.
    The correct logic should be “skip when not Bd and not album‑level”.

  2. The rule argument is lost when invoking the parser:

    ret = parser(album, None, parser)  # <‑‑ `parser` instead of `rule`
-        for rule, parser in self.parser_list:
-            if rule != 'Bd' or not rule.startswith('A'):
-                continue
+        for rule, parser in self.parser_list:
+            # keep only base‑dir rule or album‑level rules (start with 'A')
+            if rule != 'Bd' and not rule.startswith('A'):
+                continue
 ...
-            ret = parser(album, None, parser)
+            ret = parser(album, None, rule)
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)

🧹 Nitpick comments (4)
src/jmcomic/jm_option.py (4)

60-67: Import typing names explicitly instead of relying on * imports

self.parser_list: List[Tuple[str, Callable]] uses List, Tuple, and Callable, but these names are currently brought into scope only via the wildcard import on line 1.
Wildcard imports make it impossible for static analysers (and readers) to determine where symbols come from, and they are the reason Ruff is raising F405 warnings.

-from .jm_client_impl import *
+from .jm_client_impl import *   # keep if really needed
+from typing import List, Tuple, Callable
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


66-66: List may be undefined, or defined from star imports

(F405)


66-66: Tuple may be undefined, or defined from star imports

(F405)


66-66: Callable may be undefined, or defined from star imports

(F405)


73-84: Minor: avoid multiple str()/fix_windir_name passes

Inside the loop you call str() in several parsers and afterwards wrap everything in fix_windir_name.
Consider moving the fix_windir_name call inside the individual parsers (where meaningful) or at least guarding against double–processing to avoid surprises such as "C:\\folder\\file" becoming "C__folder__file" in edge‑cases.

No action strictly required, just flagging for consideration.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


81-81: fix_windir_name may be undefined, or defined from star imports

(F405)


141-144: Null‑safety: guard against photo is None for photo‑level rules

parse_detail_rule assumes photo is non‑null when the rule starts with 'P'.
If future call‑sites ever feed a 'P...' rule while building only album paths, this will raise an AttributeError.

if rule.startswith('P') and photo is None:
    ExceptionTool.raises(f'规则"{rule}"要求Photo对象,但传入 None')
🧰 Tools
🪛 Ruff (0.8.2)

143-143: DetailEntity may be undefined, or defined from star imports

(F405)


159-167: Future extensibility: expose a registry instead of if‑chain

get_rule_parser now has an explicit if‑elif chain. Consider keeping a small registry/dictionary that maps a predicate or prefix to the parser function; this allows extensions without modifying the core method.

Not blocking for this PR.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 398e1ea and 0ec83b1.

📒 Files selected for processing (1)
  • src/jmcomic/jm_option.py (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/jmcomic/jm_option.py (3)
src/jmcomic/jm_entity.py (4)
  • JmAlbumDetail (454-566)
  • JmPhotoDetail (292-451)
  • DetailEntity (64-191)
  • get_dirname (140-165)
src/jmcomic/jm_config.py (1)
  • jm_log (373-375)
src/jmcomic/jm_exception.py (2)
  • ExceptionTool (69-191)
  • raises (83-107)
🪛 Ruff (0.8.2)
src/jmcomic/jm_option.py

66-66: List may be undefined, or defined from star imports

(F405)


66-66: Tuple may be undefined, or defined from star imports

(F405)


66-66: Callable may be undefined, or defined from star imports

(F405)


69-69: JmAlbumDetail may be undefined, or defined from star imports

(F405)


70-70: JmPhotoDetail may be undefined, or defined from star imports

(F405)


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

(F405)


81-81: fix_windir_name may be undefined, or defined from star imports

(F405)


85-85: fix_filepath may be undefined, or defined from star imports

(F405)


87-87: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


143-143: DetailEntity may be undefined, or defined from star imports

(F405)


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

(F405)

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

146-156: Edge‑case: DSL splitter silently drops consecutive separators

split_rule_dsl uses str.split, which collapses // or __ into a single separator and yields empty segments.
If the intention is to allow empty segments (e.g. for “skip level”), this is fine; otherwise add a validation step to reject empty rules to avoid mysterious “不支持的dsl” errors later.

parts = [p for p in rule_dsl.split('/') if p]   # example fix
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)

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: 0

🧹 Nitpick comments (4)
src/jmcomic/jm_option.py (4)

90-102: Add consistent path sanitization in decide_album_root_dir.

Unlike decide_image_save_dir, this method doesn't sanitize path components with fix_windir_name. For consistency, consider applying the same path sanitization logic here.

            try:
                ret = parser(album, None, rule)
            except BaseException as e:
                # noinspection PyUnboundLocalVariable
                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}')
                raise e

+           if parser != self.parse_bd_rule:
+               ret = fix_windir_name(str(ret)).strip()
            path_ls.append(str(ret))
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


74-80: Consider catching more specific exceptions.

The code catches all exceptions with BaseException, which is very broad and could mask unexpected errors. Consider catching more specific exceptions like ValueError or KeyError for format-related failures.

        for rule, parser in self.parser_list:
            try:
                path = parser(album, photo, rule)
-           except BaseException as e:
+           except (ValueError, KeyError, AttributeError) as e:
                # noinspection PyUnboundLocalVariable
                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}, photo={photo}')
                raise e
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


160-168: Consider adding more descriptive error messages.

The error message for unsupported rules is the same in both get_rule_parser and split_rule_dsl, which might make it difficult to identify the source of failures. Consider adding more context to help with debugging.

        if rule.startswith(('A', 'P')):
            return cls.parse_detail_rule

-        ExceptionTool.raises(f'不支持的rule配置: "{rule}"')
+        ExceptionTool.raises(f'不支持的rule格式: "{rule}" (需要包含"{{"或以A/P开头)')
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


142-144: Consider defensive programming in parse_detail_rule.

The parse_detail_rule method doesn't check if detail is None before accessing it, which could happen if both album and photo are None or if the rule starts with 'P' but photo is None.

    @classmethod
    def parse_detail_rule(cls, album, photo, rule: str):
        detail = album if rule.startswith('A') else photo
+       if detail is None:
+           ExceptionTool.raises(f'Cannot apply rule {rule}: required {"album" if rule.startswith("A") else "photo"} is None')
        return str(DetailEntity.get_dirname(detail, rule[1:]))
🧰 Tools
🪛 Ruff (0.8.2)

144-144: DetailEntity may be undefined, or defined from star imports

(F405)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ec83b1 and c088c06.

📒 Files selected for processing (1)
  • src/jmcomic/jm_option.py (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/jmcomic/jm_option.py (4)
src/jmcomic/jm_toolkit.py (2)
  • JmcomicText (6-334)
  • parse_to_abspath (226-227)
src/jmcomic/jm_entity.py (3)
  • JmAlbumDetail (454-566)
  • DetailEntity (64-191)
  • get_dirname (140-165)
src/jmcomic/jm_config.py (1)
  • jm_log (373-375)
src/jmcomic/jm_exception.py (2)
  • ExceptionTool (69-191)
  • raises (83-107)
🪛 Ruff (0.8.2)
src/jmcomic/jm_option.py

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

(F405)


67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


70-70: JmAlbumDetail may be undefined, or defined from star imports

(F405)


71-71: JmPhotoDetail may be undefined, or defined from star imports

(F405)


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

(F405)


82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


86-86: fix_filepath may be undefined, or defined from star imports

(F405)


88-88: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


144-144: DetailEntity may be undefined, or defined from star imports

(F405)


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

(F405)

🔇 Additional comments (5)
src/jmcomic/jm_option.py (5)

131-139: LGTM: The f-string rule parser implementation correctly handles album properties.

The parse_f_string_rule implementation now correctly:

  1. Updates properties with album properties using album.getproperties()
  2. Uses format(**properties) to correctly handle named placeholders

81-84: Good addition of special handling for base directory paths.

Nice improvement to only apply fix_windir_name to non-base-directory paths, preserving the original base directory path structure.

🧰 Tools
🪛 Ruff (0.8.2)

82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


105-126: Good refactoring of rule solver into parser approach.

The refactoring from the old solver tuple-based abstraction to a direct parser function approach significantly improves code clarity and maintainability.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


67-67: The type annotation improves code clarity.

Good use of type annotations for parser_list which documents the expected structure of rule-parser pairs.

🧰 Tools
🪛 Ruff (0.8.2)

67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


170-172: LGTM: Simplified apply_rule_directly implementation.

The direct parser function call approach simplifies the code and improves maintainability by removing an unnecessary abstraction layer.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c088c06 and 3851d15.

📒 Files selected for processing (2)
  • src/jmcomic/jm_option.py (3 hunks)
  • src/jmcomic/jm_plugin.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/jmcomic/jm_option.py (4)
src/jmcomic/jm_toolkit.py (2)
  • JmcomicText (6-334)
  • parse_to_abspath (226-227)
src/jmcomic/jm_entity.py (4)
  • JmAlbumDetail (454-566)
  • get_properties_dict (167-191)
  • DetailEntity (64-191)
  • get_dirname (140-165)
src/jmcomic/jm_config.py (1)
  • jm_log (373-375)
src/jmcomic/jm_exception.py (2)
  • ExceptionTool (69-191)
  • raises (83-107)
🪛 Ruff (0.8.2)
src/jmcomic/jm_option.py

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

(F405)


67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


70-70: JmAlbumDetail may be undefined, or defined from star imports

(F405)


71-71: JmPhotoDetail may be undefined, or defined from star imports

(F405)


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

(F405)


82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


86-86: fix_filepath may be undefined, or defined from star imports

(F405)


88-88: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


101-101: fix_windir_name may be undefined, or defined from star imports

(F405)


105-105: fix_filepath may be undefined, or defined from star imports

(F405)


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

(F405)


145-145: DetailEntity may be undefined, or defined from star imports

(F405)


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

(F405)

🔇 Additional comments (14)
src/jmcomic/jm_option.py (14)

61-61: Proper naming of the base directory rule constant.

The constant RULE_BASE_DIR is well-defined and provides a clear reference for the base directory rule type.


67-67: Improved type annotation for parser list.

The type annotation for parser_list provides clear documentation about the data structure: a list of tuples containing a rule string and a parser function.

🧰 Tools
🪛 Ruff (0.8.2)

67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


74-77: Clean implementation of rule parsing.

The code now directly calls the parser function with the relevant context (album, photo, rule), making the code more maintainable and easier to understand.


79-79: More descriptive error logging.

The error log message now includes the rule that failed, providing more context for debugging.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


81-83: Proper handling of Windows directory names.

The code correctly applies fix_windir_name only when the parser is not the base directory parser, which is important because the base directory is already validated.

🧰 Tools
🪛 Ruff (0.8.2)

82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


90-96: Consistent implementation pattern across decision methods.

The decide_album_root_dir method follows the same pattern as decide_image_save_dir, with appropriate filtering for album-specific rules.


107-127: Simplified rule solver list generation.

The get_rule_solver_list method has been improved to:

  1. Process each rule and assign the appropriate parser
  2. Handle the base directory rule case explicitly
  3. Return a list of (rule, parser) pairs for direct use

This approach is more maintainable than the previous solver approach.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


129-132: Clean implementation of base directory parser.

The parse_bd_rule method is simple and focused, handling only the base directory rule case.


133-141: Well-structured f-string rule parser.

The parse_f_string_rule method properly:

  1. Collects properties from both album and photo when available
  2. Uses format(**properties) to correctly apply named placeholders
  3. Returns the formatted string

This implementation properly handles the format string placeholders.


142-146: Concise detail rule parser.

The parse_detail_rule method efficiently:

  1. Determines whether to use album or photo based on the rule prefix
  2. Delegates to DetailEntity.get_dirname for actual processing
  3. Returns the result as a string

This implementation correctly handles the detail rule case.

🧰 Tools
🪛 Ruff (0.8.2)

145-145: DetailEntity may be undefined, or defined from star imports

(F405)


148-159: Simplified rule DSL splitting.

The split_rule_dsl method now has a clearer structure:

  1. Handle base directory rule as a special case
  2. Split by '/' if present
  3. Split by '_' if present
  4. Raise an exception for unsupported rule formats

This approach is more maintainable than the previous implementation.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


160-169: Improved rule parser selection.

The get_rule_parser method efficiently:

  1. Detects f-string rules by checking for '{'
  2. Detects detail rules by checking for 'A' or 'P' prefix
  3. Raises an exception for unsupported rule formats

This approach is cleaner and more maintainable than the previous implementation.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


171-174: Simplified direct rule application.

The apply_rule_directly method now directly calls the parser function, eliminating the need for the previous solver unpacking logic.


448-449: Added type checker suppression for client domain method.

The type checker suppression is added to the decide_client_domain method to prevent false positives when working with different client types.

🧰 Tools
🪛 Ruff (0.8.2)

449-449: List may be undefined, or defined from star imports

(F405)

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: 0

🧹 Nitpick comments (2)
src/jmcomic/jm_option.py (2)

107-127: Method naming could be more consistent with the refactoring

The method has been renamed from get_role_solver_list to get_rule_solver_list, but since you're now returning parser functions rather than solvers, a more accurate name would be get_rule_parser_list.

-    def get_rule_solver_list(self, rule_dsl: str):
+    def get_rule_parser_list(self, rule_dsl: str):

Don't forget to update any references to this method if you make this change.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


74-103: Consider extracting common path-building logic

Both decide_image_save_dir and decide_album_root_dir share similar logic for:

  1. Iterating through rule-parser pairs
  2. Calling parsers and handling errors
  3. Applying fix_windir_name
  4. Building paths

Consider extracting this to a private helper method to reduce duplication.

+    def _build_path_from_rules(self, album, photo, skip_album_rules=False) -> str:
+        path_ls = []
+        for rule, parser in self.parser_list:
+            if skip_album_rules and (rule == self.RULE_BASE_DIR or rule.startswith('A')):
+                continue
+            
+            try:
+                path = parser(album, photo, rule)
+            except BaseException as e:
+                # noinspection PyUnboundLocalVariable
+                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}, photo={photo}')
+                raise e
+            if parser != self.parse_bd_rule:
+                path = fix_windir_name(str(path)).strip()
+
+            path_ls.append(path)
+
+        return fix_filepath('/'.join(path_ls), is_dir=True)
+
     def decide_image_save_dir(self,
                               album: JmAlbumDetail,
                               photo: JmPhotoDetail,
                               ) -> str:
-        path_ls = []
-        for rule, parser in self.parser_list:
-            try:
-                path = parser(album, photo, rule)
-            except BaseException as e:
-                # noinspection PyUnboundLocalVariable
-                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}, photo={photo}')
-                raise e
-            if parser != self.parse_bd_rule:
-                path = fix_windir_name(str(path)).strip()
-
-            path_ls.append(path)
-
-        return fix_filepath('/'.join(path_ls), is_dir=True)
+        return self._build_path_from_rules(album, photo)

     def decide_album_root_dir(self, album: JmAlbumDetail) -> str:
-        path_ls = []
-        for rule, parser in self.parser_list:
-            if rule == self.RULE_BASE_DIR or rule.startswith('A'):
-                continue
-
-            try:
-                path = parser(album, None, rule)
-            except BaseException as e:
-                # noinspection PyUnboundLocalVariable
-                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}')
-                raise e
-            if parser != self.parse_bd_rule:
-                path = fix_windir_name(str(path)).strip()
-
-            path_ls.append(path)
-
-        return fix_filepath('/'.join(path_ls), is_dir=True)
+        return self._build_path_from_rules(album, None, skip_album_rules=True)
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


86-86: fix_filepath may be undefined, or defined from star imports

(F405)


88-88: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


101-101: fix_windir_name may be undefined, or defined from star imports

(F405)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3851d15 and b3a122f.

📒 Files selected for processing (2)
  • src/jmcomic/jm_option.py (3 hunks)
  • src/jmcomic/jm_plugin.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/jmcomic/jm_plugin.py
🧰 Additional context used
🪛 Ruff (0.8.2)
src/jmcomic/jm_option.py

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

(F405)


67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


70-70: JmAlbumDetail may be undefined, or defined from star imports

(F405)


71-71: JmPhotoDetail may be undefined, or defined from star imports

(F405)


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

(F405)


82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


86-86: fix_filepath may be undefined, or defined from star imports

(F405)


88-88: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


101-101: fix_windir_name may be undefined, or defined from star imports

(F405)


105-105: fix_filepath may be undefined, or defined from star imports

(F405)


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

(F405)


145-145: DetailEntity may be undefined, or defined from star imports

(F405)


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

(F405)


173-173: fix_windir_name may be undefined, or defined from star imports

(F405)

🔇 Additional comments (9)
src/jmcomic/jm_option.py (9)

61-67: LGTM - Refactoring from solver tuples to parser functions

The refactoring changes solver_list to parser_list with a more straightforward (rule, parser) tuple structure, simplifying the design.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


74-84: Code looks good, implementation correctly adapted to use parser functions

This implementation properly iterates over (rule, parser) pairs and calls the parser function directly with appropriate parameters. The special handling for the base directory rule is also correctly implemented.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


82-82: fix_windir_name may be undefined, or defined from star imports

(F405)


90-103: LGTM - Consistent implementation with decide_image_save_dir

This method follows the same refactoring pattern as decide_image_save_dir, maintaining consistency in the codebase.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


101-101: fix_windir_name may be undefined, or defined from star imports

(F405)


133-141: Correct implementation of parse_f_string_rule

Good fix for the issues identified in the previous review:

  1. Properly updating properties from album with album.get_properties_dict()
  2. Correctly using format(**properties) to handle named placeholders

142-146: LGTM - Clean implementation of parse_detail_rule

This method correctly handles different detail types based on the rule prefix.

🧰 Tools
🪛 Ruff (0.8.2)

145-145: DetailEntity may be undefined, or defined from star imports

(F405)


148-158: LGTM - Simplified split_rule_dsl implementation

This implementation is cleaner and easier to understand compared to the previous version.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


160-169: LGTM - get_rule_parser returns appropriate parser functions

This method correctly determines which parser function to use based on the rule format.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


171-173: LGTM - apply_rule_directly updated to use parser functions directly

The method has been updated to align with the new parser-based approach.

🧰 Tools
🪛 Ruff (0.8.2)

173-173: fix_windir_name may be undefined, or defined from star imports

(F405)


448-449: LGTM - Suppression of static analysis warnings

Adding type checking suppression is appropriate here.

🧰 Tools
🪛 Ruff (0.8.2)

449-449: List may be undefined, or defined from star imports

(F405)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/jmcomic/jm_option.py (2)

78-94: Potential exception handling improvement in error logging.

The error handling catches and re-raises the exception without transformation, which is generally unnecessary. Also, the error message directly includes album and photo objects, which might result in very long or unhelpful log messages.

            try:
                path = parser(album, photo, rule)
            except BaseException as e:
                # noinspection PyUnboundLocalVariable
-                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album={album}, photo={photo}')
+                jm_log('dir_rule', f'路径规则"{rule}"的解析出错: {e}, album_id={album.aid if album else None}, photo_id={photo.pid if photo else None}')
                raise e
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


91-91: fix_windir_name may be undefined, or defined from star imports

(F405)


61-165: Consider adding documentation to new parser methods.

The newly added parser methods (parse_bd_rule, parse_f_string_rule, parse_detail_rule) lack documentation explaining their purpose, parameters, and return values. Since they form the core of your new parsing approach, proper documentation would help maintainers understand their usage.

    # noinspection PyUnusedLocal
    def parse_bd_rule(self, album, photo, rule):
+        """
+        Parse the base directory rule.
+        
+        :param album: Album detail or None
+        :param photo: Photo detail or None
+        :param rule: The rule string (always RULE_BASE_DIR)
+        :return: The base directory path
+        """
        return self.base_dir

    @classmethod
    def parse_f_string_rule(cls, album, photo, rule: str):
+        """
+        Parse a rule using string formatting with album and photo properties.
+        
+        :param album: Album detail or None
+        :param photo: Photo detail or None
+        :param rule: The rule string containing format placeholders
+        :return: The formatted string
+        """
        properties = {}
        if album:
            properties.update(album.get_properties_dict())
        if photo:
            properties.update(photo.get_properties_dict())
        return rule.format(**properties)

    @classmethod
    def parse_detail_rule(cls, album, photo, rule: str):
+        """
+        Parse a rule that refers to a field from album or photo details.
+        
+        :param album: Album detail or None
+        :param photo: Photo detail or None
+        :param rule: The rule string starting with 'A' or 'P'
+        :return: The string value of the referenced field
+        """
        detail = album if rule.startswith('A') else photo
        return str(DetailEntity.get_dirname(detail, rule[1:]))
🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


70-70: JmAlbumDetail may be undefined, or defined from star imports

(F405)


71-71: JmPhotoDetail may be undefined, or defined from star imports

(F405)


75-75: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


91-91: fix_windir_name may be undefined, or defined from star imports

(F405)


95-95: fix_filepath may be undefined, or defined from star imports

(F405)


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

(F405)


135-135: DetailEntity may be undefined, or defined from star imports

(F405)


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

(F405)


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

(F405)


165-165: fix_windir_name may be undefined, or defined from star imports

(F405)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 575a7ad and 1589329.

📒 Files selected for processing (1)
  • src/jmcomic/jm_option.py (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/jmcomic/jm_option.py (4)
src/jmcomic/jm_toolkit.py (2)
  • JmcomicText (6-334)
  • parse_to_abspath (226-227)
src/jmcomic/jm_entity.py (4)
  • JmAlbumDetail (454-566)
  • get_properties_dict (167-191)
  • DetailEntity (64-191)
  • get_dirname (140-165)
src/jmcomic/jm_config.py (1)
  • jm_log (373-375)
src/jmcomic/jm_exception.py (2)
  • ExceptionTool (69-191)
  • raises (83-107)
🪛 Ruff (0.8.2)
src/jmcomic/jm_option.py

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

(F405)


67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


70-70: JmAlbumDetail may be undefined, or defined from star imports

(F405)


71-71: JmPhotoDetail may be undefined, or defined from star imports

(F405)


75-75: JmAlbumDetail may be undefined, or defined from star imports

(F405)


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

(F405)


91-91: fix_windir_name may be undefined, or defined from star imports

(F405)


95-95: fix_filepath may be undefined, or defined from star imports

(F405)


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

(F405)


135-135: DetailEntity may be undefined, or defined from star imports

(F405)


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

(F405)


165-165: fix_windir_name may be undefined, or defined from star imports

(F405)

🔇 Additional comments (9)
src/jmcomic/jm_option.py (9)

67-67: Looks good! Renamed solver_list to parser_list for better clarity.

The refactoring from solver_list to parser_list clarifies the intention of this structure, making it more maintainable. The type hint also provides better documentation of the structure.

🧰 Tools
🪛 Ruff (0.8.2)

67-67: List may be undefined, or defined from star imports

(F405)


67-67: Tuple may be undefined, or defined from star imports

(F405)


67-67: Callable may be undefined, or defined from star imports

(F405)


73-76: Good refactoring that reduces code duplication.

The refactoring centralizes the path building logic in a helper method, eliminating duplicate code between the two decision methods.

🧰 Tools
🪛 Ruff (0.8.2)

75-75: JmAlbumDetail may be undefined, or defined from star imports

(F405)


97-118: Simplified rule parsing logic looks good.

The refactored method now builds a cleaner list of (rule, parser) pairs instead of the more complex tuple structure used before, making the code easier to understand and maintain.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


119-122: Simple and focused base directory parser function.

This extracted method has a clear, single responsibility, which is good for maintainability.


123-131: Improved string formatting with proper property handling.

The implementation correctly:

  1. Gathers properties from both album and photo
  2. Uses format(**properties) for named placeholders, addressing the previous review comment

138-148: Simplified rule DSL splitting looks good.

The implementation is more concise while maintaining the same functionality.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


151-159: Simplified rule parser selection looks good.

The method now directly returns the appropriate parser function rather than constructing a complex solver tuple.

🧰 Tools
🪛 Ruff (0.8.2)

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

(F405)


161-165: Good fallback for missing album context.

The method now correctly falls back to photo.from_album if album is None, which supports the refactoring to use parser functions directly.

🧰 Tools
🪛 Ruff (0.8.2)

165-165: fix_windir_name may be undefined, or defined from star imports

(F405)


440-441: Added type checker suppression annotation.

The suppression is appropriate for this method that uses dynamic typing in ways that static type checkers may not fully understand.

🧰 Tools
🪛 Ruff (0.8.2)

441-441: List may be undefined, or defined from star imports

(F405)

Comment on lines +132 to 136
@classmethod
def parse_detail_rule(cls, album, photo, rule: str):
detail = album if rule.startswith('A') else photo
return str(DetailEntity.get_dirname(detail, rule[1:]))

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add null safety checks for detail rule parsing.

The method could throw an exception if it tries to get a directory name from a null album or photo.

    @classmethod
    def parse_detail_rule(cls, album, photo, rule: str):
        detail = album if rule.startswith('A') else photo
+        if detail is None:
+            ExceptionTool.raises(f'无法解析规则"{rule}": {"album" if rule.startswith("A") else "photo"} 为 None')
        return str(DetailEntity.get_dirname(detail, rule[1:]))
📝 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
@classmethod
def parse_detail_rule(cls, album, photo, rule: str):
detail = album if rule.startswith('A') else photo
return str(DetailEntity.get_dirname(detail, rule[1:]))
@classmethod
def parse_detail_rule(cls, album, photo, rule: str):
detail = album if rule.startswith('A') else photo
if detail is None:
ExceptionTool.raises(f'无法解析规则"{rule}": {"album" if rule.startswith("A") else "photo"} 为 None')
return str(DetailEntity.get_dirname(detail, rule[1:]))
🧰 Tools
🪛 Ruff (0.8.2)

135-135: DetailEntity may be undefined, or defined from star imports

(F405)

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