Skip to content

v2.6.15: 日志基于logging重构; README更新Python版本要求 (>=3.9); 新增contributing.md#516

Merged
hect0x7 merged 14 commits intomasterfrom
dev
Feb 23, 2026
Merged

v2.6.15: 日志基于logging重构; README更新Python版本要求 (>=3.9); 新增contributing.md#516
hect0x7 merged 14 commits intomasterfrom
dev

Conversation

@hect0x7
Copy link
Owner

@hect0x7 hect0x7 commented Feb 23, 2026

Summary by CodeRabbit

  • New Features

    • Enhanced default logging: richer timestamp/topic/thread format, topic-based filtering, default console handler, and forwarding of exception objects with backward-compatible handling.
  • Documentation

    • Expanded logging tutorials showing two customization approaches and updated examples and references.
    • Revised README and localized quick-guides; recommend Python 3.12+ (minimum compatibility noted).
    • Added a comprehensive contributor guide.
  • Chores

    • Package version bumped to 2.6.15.
    • Raised minimum Python requirement to >=3.9; CI updated to include Python 3.14.

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors logging to a module-level Python logger with topic-aware filtering and exception support, updates jm_log signature and related callers to accept an optional Exception, amends docs/examples for the new logging API, raises minimum Python to >=3.9 (recommends 3.12+), bumps version to 2.6.15, updates CI matrix, and adds a contributor guide.

Changes

Cohort / File(s) Summary
Logging core
src/jmcomic/jm_config.py
Adds jm_logger and setup_default_jm_logger(); implements default_jm_logging(topic, msg, e=None); updates JmModuleConfig.jm_log(cls, topic, msg, e=None) to pass e when supported; invokes default logger at import.
Plugin & option error paths
src/jmcomic/jm_plugin.py, src/jmcomic/jm_option.py
Replaces inline filter with TopicFilter logging.Filter and dedupes filters; logs Exception objects via jm_log(...) instead of printing tracebacks; safe plugin exceptions now logged.
Exception class
src/jmcomic/jm_exception.py
Calls super().__init__(msg) in JmcomicException.__init__ so Exception.args include the message.
Docs: logging tutorials
assets/docs/sources/tutorial/4_module_custom.md, assets/docs/sources/tutorial/11_log_custom.md
Examples updated to show my_custom_log(topic, msg, e=None), use setup_default_jm_logger() / disable_jm_log(), recommend configuring standard jmcomic logger; fixed code fence to python and clarified interception approaches.
README translations
README.md, assets/readme/README-en.md, assets/readme/README-jp.md, assets/readme/README-kr.md
Adds quick-guide links and updates Python guidance to recommend 3.12+ while noting minimum compatibility 3.9 and EOL note for older versions.
Versioning & packaging
src/jmcomic/__init__.py, pyproject.toml, setup.py
Bumps __version__ 2.6.14 → 2.6.15; raises requires-python to >=3.9; removes 3.7/3.8 classifiers and adds 3.14 classifier.
CI
.github/workflows/test_api.yml
Updates GitHub Actions Python matrix: removes 3.11, adds 3.14 (matrix: 3.9, 3.10, 3.13, 3.14).
Contributing guide
.github/CONTRIBUTING.md
Adds a comprehensive contributor guide covering workflow, tests, PR/release rules, and coding/logging/exception conventions.
Docs snippet formatting
assets/docs/sources/tutorial/11_log_custom.md
Changes code fence to python, shows disable_jm_log() usage and clarifies two interception approaches in the section header.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Caller as rgba(60,120,216,0.5) Caller
participant JmLog as rgba(76,175,80,0.5) JmModuleConfig.jm_log
participant Logger as rgba(255,193,7,0.5) jm_logger
participant Filter as rgba(156,39,176,0.5) TopicFilter
participant Handler as rgba(233,30,99,0.5) Handler/FileHandler
Caller->>JmLog: jm_log(topic, msg, e?)
JmLog->>Logger: logger.log(level, msg, extra={topic}, exc_info=e)
Logger->>Filter: apply topic whitelist
Logger->>Handler: emit record (console/file)
Handler-->>Caller: output persisted/emitted

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hop through logs with topic cheer,

exceptions tucked and handlers near.
Filters listen, messages sing,
version raised — the pipelines spring.
Hooray for logs that tell true things!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the three main changes: logging refactor to use Python's logging module, README Python version requirement update to >=3.9, and addition of contributing.md file.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

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

🧹 Nitpick comments (3)
src/jmcomic/jm_config.py (3)

27-27: msg is untyped and e uses implicit Optional — two annotation gaps flagged by Ruff (RUF013).

default_jm_logging accepts msg as either str or BaseException (line 29 checks isinstance(msg, BaseException)), but the parameter has no annotation. Similarly, e: Exception = None is an implicit Optional[Exception].

♻️ Proposed annotation fix
-def default_jm_logging(topic: str, msg, e: Exception = None):
+def default_jm_logging(topic: str, msg: Union[str, BaseException], e: Optional[Exception] = None):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` at line 27, The function default_jm_logging has
missing type annotations: annotate msg as Union[str, BaseException] and annotate
e as Optional[Exception] to remove the implicit Optional warning; update the
function signature for default_jm_logging to use these types and add the
necessary typing imports (Union, Optional) at the top of the module so type
checkers and Ruff (RUF013) are satisfied.

16-24: setup_default_jm_logger adds a handler at library import time — deviates from Python library logging best practice.

Python's logging HOWTO recommends that libraries add only a NullHandler and leave handler configuration to application code. The if not jm_logger.handlers: guard is a reasonable mitigation for backward compatibility (as noted in the comment), but be aware this means importing jmcomic in an application that hasn't pre-configured the "jmcomic" logger will always attach a StreamHandler and set the level to INFO. This could interfere with host applications that rely on propagate=True and root-logger-level control.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, setup_default_jm_logger
currently attaches a StreamHandler and sets jm_logger level at import, which
violates library logging best practices; change it to only add a
logging.NullHandler (and do not call jm_logger.setLevel) when no handlers exist
so the library does not configure application logging, and keep the import-time
guard and jm_logger symbol but replace the StreamHandler/formatter/level actions
with adding logging.NullHandler() to jm_logger to preserve backward
compatibility without forcing console output.

408-410: jm_log's msg: str annotation contradicts its documented shortcut usage; also implicit Optional (Ruff RUF013).

The method is designed to accept an exception object as msg (e.g., jm_log('plugin.exception', e)), but the signature declares msg: str. This misleads callers and type checkers. The e: Exception = None should also be Optional[Exception] = None.

♻️ Proposed annotation fix
-    def jm_log(cls, topic: str, msg: str, e: Exception = None):
+    def jm_log(cls, topic: str, msg: Union[str, BaseException], e: Optional[Exception] = None):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 408 - 410, The jm_log signature
mis-annotates parameters: change msg: str to accept non-string exceptions (e.g.,
msg: Any or msg: Union[str, Exception]) and annotate the exception parameter as
e: Optional[Exception] = None (import Optional/Any or Union from typing); update
the function signature on jm_log and add the necessary typing import so
cls.EXECUTOR_LOG(topic, msg, e) remains unchanged and Ruff RUF013 is satisfied
while callers like jm_log('plugin.exception', e) are type-correct.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99f5c6d and 6b16174.

📒 Files selected for processing (8)
  • CONTRIBUTING.md
  • assets/docs/sources/tutorial/11_log_custom.md
  • assets/docs/sources/tutorial/4_module_custom.md
  • src/jmcomic/__init__.py
  • src/jmcomic/jm_config.py
  • src/jmcomic/jm_exception.py
  • src/jmcomic/jm_option.py
  • src/jmcomic/jm_plugin.py
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CONTRIBUTING.md`:
- Line 55: Replace the incorrect use of the particle "的" with the adverbial
particle "地" in the two phrases flagged by the reviewer: change "最新的推流" to
"最新地推流" and change "合理的命名" to "合理地命名" in CONTRIBUTING.md; locate the exact
phrases (quoted above) and update them so the verb-modifying phrases use "地".

In `@src/jmcomic/jm_config.py`:
- Line 21: The logging Formatter in jm_config.py uses '%(topic)s' causing
KeyError when jm_logger.* is called without extra; update the logging.Formatter
instantiation (the formatter variable) to supply a defaults mapping (e.g.
include a 'topic' default) so records missing 'topic' won't error — adjust the
call that creates formatter in jm_config.py (where logging.Formatter(...) is
used) to pass defaults={'topic': '<default value or empty string>'} compatible
with Python 3.8+.

In `@src/jmcomic/jm_plugin.py`:
- Around line 558-567: TopicFilter is defined inside invoke so each call creates
a distinct class and isinstance checks against jm_logger.filters fail to remove
prior instances; move TopicFilter out of invoke to module scope (or make it a
nested class on a stable object) so its identity is stable, then retain the
deduplication logic that filters jm_logger.filters by isinstance(f, TopicFilter)
and re-add a single TopicFilter instance; update references in log_topic_filter
/ JmOption after_init to use the module-level TopicFilter.

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Line 27: The function default_jm_logging has missing type annotations:
annotate msg as Union[str, BaseException] and annotate e as Optional[Exception]
to remove the implicit Optional warning; update the function signature for
default_jm_logging to use these types and add the necessary typing imports
(Union, Optional) at the top of the module so type checkers and Ruff (RUF013)
are satisfied.
- Around line 16-24: setup_default_jm_logger currently attaches a StreamHandler
and sets jm_logger level at import, which violates library logging best
practices; change it to only add a logging.NullHandler (and do not call
jm_logger.setLevel) when no handlers exist so the library does not configure
application logging, and keep the import-time guard and jm_logger symbol but
replace the StreamHandler/formatter/level actions with adding
logging.NullHandler() to jm_logger to preserve backward compatibility without
forcing console output.
- Around line 408-410: The jm_log signature mis-annotates parameters: change
msg: str to accept non-string exceptions (e.g., msg: Any or msg: Union[str,
Exception]) and annotate the exception parameter as e: Optional[Exception] =
None (import Optional/Any or Union from typing); update the function signature
on jm_log and add the necessary typing import so cls.EXECUTOR_LOG(topic, msg, e)
remains unchanged and Ruff RUF013 is satisfied while callers like
jm_log('plugin.exception', e) are type-correct.

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

♻️ Duplicate comments (2)
CONTRIBUTING.md (2)

55-55: ⚠️ Potential issue | 🟡 Minor

Grammar nit: 最新的推流最新地推流 (adverbial , not attributive ).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CONTRIBUTING.md` at line 55, Replace the grammatically incorrect phrase
"最新的推流" with the adverbial form "最新地推流" in the CONTRIBUTING.md sentence "💡
请前往你个人仓库的 **Actions** 面板,确保最新的推流全部亮起绿灯。" so it reads "💡 请前往你个人仓库的 **Actions**
面板,确保最新地推流全部亮起绿灯。"; update only that phrase to correct the adverbial usage.

86-86: ⚠️ Potential issue | 🟡 Minor

Grammar nit: 合理的命名合理地命名 (adverbial , not attributive ).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CONTRIBUTING.md` at line 86, Replace the grammatically incorrect phrase
"合理的命名" with the adverbial form "合理地命名" in the CONTRIBUTING.md sentence that
currently reads "在为 `jmcomic` 编写或改造代码时,请尽量增加类型注解 (Type hints)
并遵守合理的命名。除此之外,为确保全机队的统一性,请尽量遵循以下架构规范:"; update that exact string so it becomes
"并遵守合理地命名" to correct the grammar.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b16174 and 54841de.

📒 Files selected for processing (7)
  • CONTRIBUTING.md
  • README.md
  • assets/readme/README-en.md
  • assets/readme/README-jp.md
  • assets/readme/README-kr.md
  • pyproject.toml
  • setup.py
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CONTRIBUTING.md`:
- Line 38: The internal Markdown anchor for the heading "🛠️ 引擎改装规范 (Coding
Guidelines)" is broken because GitHub strips emoji and produces a different
anchor; fix it by adding an explicit HTML anchor immediately above that heading
(e.g., <a name="coding-guidelines"></a>) and then update the existing link that
currently points to the emoji-based anchor to use (`#coding-guidelines`) instead;
locate the heading text "🛠️ 引擎改装规范 (Coding Guidelines)" and the link text
referencing that heading and change them as described.

---

Duplicate comments:
In `@CONTRIBUTING.md`:
- Line 55: Replace the grammatically incorrect phrase "最新的推流" with the adverbial
form "最新地推流" in the CONTRIBUTING.md sentence "💡 请前往你个人仓库的 **Actions**
面板,确保最新的推流全部亮起绿灯。" so it reads "💡 请前往你个人仓库的 **Actions** 面板,确保最新地推流全部亮起绿灯。";
update only that phrase to correct the adverbial usage.
- Line 86: Replace the grammatically incorrect phrase "合理的命名" with the adverbial
form "合理地命名" in the CONTRIBUTING.md sentence that currently reads "在为 `jmcomic`
编写或改造代码时,请尽量增加类型注解 (Type hints) 并遵守合理的命名。除此之外,为确保全机队的统一性,请尽量遵循以下架构规范:"; update
that exact string so it becomes "并遵守合理地命名" to correct the grammar.

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 info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54841de and a15ad2d.

📒 Files selected for processing (3)
  • .github/workflows/test_api.yml
  • pyproject.toml
  • setup.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • pyproject.toml
  • setup.py
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/test_api.yml:
- Line 19: Update the GitHub Actions matrix for the python-version key by
removing '3.9' from the array (python-version: ['3.10','3.13','3.14']) to
reflect that 3.9 is EOL; if you intentionally must keep 3.9 as the supported
minimum, instead add an explicit inline comment next to the python-version array
explaining why 3.9 is retained and that it is tested intentionally despite being
EOL. Ensure you only modify the python-version array entry referenced in the
workflow.

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

♻️ Duplicate comments (1)
CONTRIBUTING.md (1)

38-38: ⚠️ Potential issue | 🟡 Minor

Broken internal anchor on line 38 is still unresolved.

The link #%EF%B8%8F-引擎改装规范-coding-guidelines (which decodes to only the variation-selector codepoint U+FE0F) does not match the anchor GitHub actually generates for ## 🛠️ 引擎改装规范 (Coding Guidelines). GitHub strips emoji codepoints and turns adjacent spaces into hyphens, producing #-引擎改装规范-coding-guidelines. The markdownlint MD051 warning also confirms this.

The most robust fix is an explicit HTML anchor:

🔗 Proposed fix
-在动工之前,请务必仔细阅读本文档末尾的《[🛠️ 引擎改装规范(Coding Guidelines)](#%EF%B8%8F-引擎改装规范-coding-guidelines)》 部分。
+在动工之前,请务必仔细阅读本文档末尾的《[🛠️ 引擎改装规范(Coding Guidelines)](`#coding-guidelines`)》 部分。

And on the target heading:

-## 🛠️ 引擎改装规范 (Coding Guidelines)
+## 🛠️ 引擎改装规范 (Coding Guidelines) <a id="coding-guidelines"></a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CONTRIBUTING.md` at line 38, The internal anchor link
`#%EF%B8%8F-引擎改装规范-coding-guidelines` is broken; update the link and target by
adding an explicit HTML anchor before the heading and point the link to that
anchor: insert an ID like `<a id="coding-guidelines"></a>` immediately above the
heading `## 🛠️ 引擎改装规范 (Coding Guidelines)` and change the link on line 38 to
`#coding-guidelines` (or a clearly named anchor of your choice) so
GitHub-generated anchors and markdownlint MD051 are avoided.
🧹 Nitpick comments (2)
src/jmcomic/jm_plugin.py (1)

297-298: Type mismatch: e (Exception) is passed as msg (typed str) — works at runtime but is type-unsafe.

jm_log('req.error', e) passes the exception as the msg parameter. This works because default_jm_logging has the isinstance(msg, BaseException) shorthand (jm_config.py line 29), but it silently violates the declared msg: str type annotation on jm_log.

Consider using the explicit three-argument form for clarity:

♻️ More explicit invocation
-                    jm_log('req.error', e)
+                    jm_log('req.error', str(e), e)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_plugin.py` around lines 297 - 298, Replace the type-unsafe
call jm_log('req.error', e) with the explicit three-argument form so the first
arg stays the topic (string), the second is a string message (e.g., str(e)) and
the third passes the exception object; update the call to jm_log('req.error',
str(e), e) so it matches jm_log's typed signature and still provides the
exception for default_jm_logging to inspect.
src/jmcomic/jm_config.py (1)

27-36: Well-structured logging function with exception shorthand support.

The isinstance(msg, BaseException) shorthand and the exc_info=e usage are both correct (Python 3.5+ supports passing exception instances to exc_info).

Minor nit from static analysis: e: Exception = None should be e: Optional[Exception] = None per PEP 484.

♻️ Optional type annotation fix
-def default_jm_logging(topic: str, msg, e: Exception = None):
+def default_jm_logging(topic: str, msg, e: Optional[Exception] = None):

This requires importing Optional from typing (or using Exception | None on Python 3.10+).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 27 - 36, Update the default_jm_logging
signature to use an Optional type for the exception parameter: change "def
default_jm_logging(topic: str, msg, e: Exception = None):" to "def
default_jm_logging(topic: str, msg, e: Optional[Exception] = None):" (or "e:
Exception | None = None" if the codebase targets Python 3.10+), and add the
corresponding import "from typing import Optional" at the top of the module so
the Optional annotation is defined; leave the body (isinstance(msg,
BaseException) handling and jm_logger.error/jm_logger.info calls) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/jmcomic/jm_config.py`:
- Around line 16-24: The Formatter is using the Python 3.10-only "defaults"
kwarg which breaks on 3.9; in setup_default_jm_logger remove the defaults
argument from logging.Formatter and instead create and add a logging.Filter
(e.g., subclass or lambda-style filter) that ensures every LogRecord has a topic
attribute (set record.topic = '' when missing) and attach that filter to the
handler (or jm_logger) before calling handler.setFormatter(...); keep
jm_logger.addHandler(handler) and jm_logger.setLevel(logging.INFO) as-is.
- Around line 407-410: The jm_log method now calls EXECUTOR_LOG(topic, msg, e)
which will break custom two-argument EXECUTOR_LOG implementations; update jm_log
to call EXECUTOR_LOG defensively by detecting or handling both signatures (e.g.,
inspect the callable signature or try calling with three args and fall back to
two args) so jm_log (and FLAG_ENABLE_JM_LOG gating) works with existing custom
loggers, referencing jm_log, EXECUTOR_LOG and FLAG_ENABLE_JM_LOG in your change;
alternatively document this as a breaking change in the release notes if you
choose not to add backward compatibility.

---

Duplicate comments:
In `@CONTRIBUTING.md`:
- Line 38: The internal anchor link `#%EF%B8%8F-引擎改装规范-coding-guidelines` is
broken; update the link and target by adding an explicit HTML anchor before the
heading and point the link to that anchor: insert an ID like `<a
id="coding-guidelines"></a>` immediately above the heading `## 🛠️ 引擎改装规范
(Coding Guidelines)` and change the link on line 38 to `#coding-guidelines` (or
a clearly named anchor of your choice) so GitHub-generated anchors and
markdownlint MD051 are avoided.

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Around line 27-36: Update the default_jm_logging signature to use an Optional
type for the exception parameter: change "def default_jm_logging(topic: str,
msg, e: Exception = None):" to "def default_jm_logging(topic: str, msg, e:
Optional[Exception] = None):" (or "e: Exception | None = None" if the codebase
targets Python 3.10+), and add the corresponding import "from typing import
Optional" at the top of the module so the Optional annotation is defined; leave
the body (isinstance(msg, BaseException) handling and
jm_logger.error/jm_logger.info calls) unchanged.

In `@src/jmcomic/jm_plugin.py`:
- Around line 297-298: Replace the type-unsafe call jm_log('req.error', e) with
the explicit three-argument form so the first arg stays the topic (string), the
second is a string message (e.g., str(e)) and the third passes the exception
object; update the call to jm_log('req.error', str(e), e) so it matches jm_log's
typed signature and still provides the exception for default_jm_logging to
inspect.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a15ad2d and 10bfae9.

📒 Files selected for processing (3)
  • CONTRIBUTING.md
  • src/jmcomic/jm_config.py
  • src/jmcomic/jm_plugin.py

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.

♻️ Duplicate comments (2)
src/jmcomic/jm_config.py (2)

16-24: Missing topic default on log records — direct jm_logger calls will produce stderr tracebacks.

The defaults kwarg was correctly removed (fixing the Python 3.10+ compatibility issue), but the suggested replacement — a logging.Filter that injects a default topic attribute — was not added. Any direct jm_logger.info(...) call without extra={'topic': ...} will hit a KeyError in the formatter, causing Handler.handleError() to dump a traceback to stderr.

Suggested fix — add a TopicFilter
 def setup_default_jm_logger():
     # 为了保持原有默认向下兼容,如果没有 handler,我们加一个控制台 handler
     if not jm_logger.handlers:
         import sys
         handler = logging.StreamHandler(sys.stdout)
         formatter = logging.Formatter('[%(asctime)s] [%(threadName)s]:【%(topic)s】%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
         handler.setFormatter(formatter)
         jm_logger.addHandler(handler)
         jm_logger.setLevel(logging.INFO)
+
+    class TopicFilter(logging.Filter):
+        def filter(self, record):
+            if not hasattr(record, 'topic'):
+                record.topic = ''
+            return True
+
+    jm_logger.addFilter(TopicFilter())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, The formatter expects a
record.topic attribute but direct jm_logger.info(...) calls can omit extra
leading to KeyError; in setup_default_jm_logger() add a logging.Filter (e.g.,
TopicFilter) that ensures every LogRecord has a default record.topic (for
example 'jm' or 'default') when missing and attach it to the handler via
handler.addFilter(...) so the formatter never fails; reference
setup_default_jm_logger, jm_logger, and the handler variable when locating where
to register the filter.

407-410: LogTopicFilterPlugin will crash — its wrapper only accepts 2 args.

This was flagged in a prior review, and the provided code snippets confirm the concrete breakage. In jm_plugin.py (lines 557–561), LogTopicFilterPlugin.invoke() wraps EXECUTOR_LOG with:

def new_jm_log(topic, msg):  # only 2 params
    ...
    old_jm_log(topic, msg)

Line 410 now calls cls.EXECUTOR_LOG(topic, msg, e) with 3 arguments → TypeError at runtime whenever the log_topic_filter plugin is active.

At minimum, the plugin wrapper in jm_plugin.py must be updated to accept and forward e:

def new_jm_log(topic, msg, e=None):
    if whitelist is not None and topic not in whitelist:
        return
    old_jm_log(topic, msg, e)
#!/bin/bash
# Find all EXECUTOR_LOG wrappers or reassignments that only accept 2 args
rg -nP 'EXECUTOR_LOG\s*=' --type=py -A5
echo "---"
# Also check for any other custom jm_log replacements
rg -nP 'def\s+\w*jm_log\w*\s*\(' --type=py -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 407 - 410, The LogTopicFilterPlugin
wrapper for EXECUTOR_LOG only accepts two parameters causing a TypeError when
jm_config.jm_log calls cls.EXECUTOR_LOG(topic, msg, e); update the wrapper in
LogTopicFilterPlugin.invoke (the function named new_jm_log that replaces
EXECUTOR_LOG / jm_log) to accept an optional third parameter (e=None) and
forward it to the original old_jm_log (i.e., change its signature to accept
topic, msg, e=None and call old_jm_log(topic, msg, e)); ensure any other custom
EXECUTOR_LOG/jm_log replacements follow the same three-argument signature to
avoid similar runtime errors.
🧹 Nitpick comments (1)
src/jmcomic/jm_config.py (1)

27-36: Solid shorthand for exception logging.

The isinstance(msg, BaseException) shorthand and the exc_info=e usage are both correct. One minor Ruff nit:

Line 27: e: Exception = None is an implicit Optional. Consider e: Optional[Exception] = None (or Exception | None on 3.10+) per PEP 484.

Same applies to line 408.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 27 - 36, Update the explicit exception
type hints to be properly optional: replace e: Exception = None with either e:
Optional[Exception] = None (and add from typing import Optional) or use Python
3.10 union syntax e: Exception | None = None; apply this change in
default_jm_logging and the other function that currently declares e: Exception =
None so both signatures correctly reflect an optional Exception type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/jmcomic/jm_config.py`:
- Around line 16-24: The formatter expects a record.topic attribute but direct
jm_logger.info(...) calls can omit extra leading to KeyError; in
setup_default_jm_logger() add a logging.Filter (e.g., TopicFilter) that ensures
every LogRecord has a default record.topic (for example 'jm' or 'default') when
missing and attach it to the handler via handler.addFilter(...) so the formatter
never fails; reference setup_default_jm_logger, jm_logger, and the handler
variable when locating where to register the filter.
- Around line 407-410: The LogTopicFilterPlugin wrapper for EXECUTOR_LOG only
accepts two parameters causing a TypeError when jm_config.jm_log calls
cls.EXECUTOR_LOG(topic, msg, e); update the wrapper in
LogTopicFilterPlugin.invoke (the function named new_jm_log that replaces
EXECUTOR_LOG / jm_log) to accept an optional third parameter (e=None) and
forward it to the original old_jm_log (i.e., change its signature to accept
topic, msg, e=None and call old_jm_log(topic, msg, e)); ensure any other custom
EXECUTOR_LOG/jm_log replacements follow the same three-argument signature to
avoid similar runtime errors.

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Around line 27-36: Update the explicit exception type hints to be properly
optional: replace e: Exception = None with either e: Optional[Exception] = None
(and add from typing import Optional) or use Python 3.10 union syntax e:
Exception | None = None; apply this change in default_jm_logging and the other
function that currently declares e: Exception = None so both signatures
correctly reflect an optional Exception type.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10bfae9 and 2bd7c4a.

📒 Files selected for processing (1)
  • src/jmcomic/jm_config.py

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.

♻️ Duplicate comments (1)
src/jmcomic/jm_config.py (1)

16-24: Filter for default topic attribute is still missing — direct jm_logger calls will fail.

The formatter at line 21 requires %(topic)s, but no filter is added to inject a default topic when users call jm_logger.info(...) directly without extra={'topic': ...}. The defaults parameter was removed (correctly, since it requires Python 3.10+), but the suggested filter-based approach from the previous review was not implemented.

Suggested fix — add a filter for default topic
 def setup_default_jm_logger():
     # 为了保持原有默认向下兼容,如果没有 handler,我们加一个控制台 handler
     if not jm_logger.handlers:
         import sys
         handler = logging.StreamHandler(sys.stdout)
         formatter = logging.Formatter('[%(asctime)s] [%(threadName)s]:【%(topic)s】%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
         handler.setFormatter(formatter)
         jm_logger.addHandler(handler)
         jm_logger.setLevel(logging.INFO)
+
+    # Ensure every record has a 'topic' attribute (compatible with Python 3.9+)
+    class TopicFilter(logging.Filter):
+        def filter(self, record):
+            if not hasattr(record, 'topic'):
+                record.topic = ''
+            return True
+
+    jm_logger.addFilter(TopicFilter())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, The formatter in
setup_default_jm_logger uses %(topic)s but no filter provides a default, so
direct jm_logger.info(...) will KeyError; fix by creating and adding a
logging.Filter (or a small subclass with a filter(self, record) method) that
sets record.topic = getattr(record, "topic", "default") (or other sane default)
and attach that filter to jm_logger (and/or the new handler) inside
setup_default_jm_logger so every LogRecord has a topic before formatting;
reference setup_default_jm_logger and jm_logger when locating where to add the
filter.
🧹 Nitpick comments (1)
src/jmcomic/jm_config.py (1)

27-36: Use Optional[Exception] for PEP 484 compliance.

The type hint e: Exception = None implicitly allows None, which PEP 484 prohibits. The same applies to line 408.

Suggested fix
+from typing import Optional
+
-def default_jm_logging(topic: str, msg, e: Exception = None):
+def default_jm_logging(topic: str, msg, e: Optional[Exception] = None):

And at line 408:

-    def jm_log(cls, topic: str, msg: str, e: Exception = None):
+    def jm_log(cls, topic: str, msg: str, e: Optional[Exception] = None):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 27 - 36, The parameter type hint e:
Exception = None is not PEP 484 compliant; update default_jm_logging to use
Optional[Exception] (e: Optional[Exception] = None) and import Optional from
typing, and make the same change for the other function in this module that
currently declares e: Exception = None so both annotations use
Optional[Exception].
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/jmcomic/jm_config.py`:
- Around line 16-24: The formatter in setup_default_jm_logger uses %(topic)s but
no filter provides a default, so direct jm_logger.info(...) will KeyError; fix
by creating and adding a logging.Filter (or a small subclass with a filter(self,
record) method) that sets record.topic = getattr(record, "topic", "default") (or
other sane default) and attach that filter to jm_logger (and/or the new handler)
inside setup_default_jm_logger so every LogRecord has a topic before formatting;
reference setup_default_jm_logger and jm_logger when locating where to add the
filter.

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Around line 27-36: The parameter type hint e: Exception = None is not PEP 484
compliant; update default_jm_logging to use Optional[Exception] (e:
Optional[Exception] = None) and import Optional from typing, and make the same
change for the other function in this module that currently declares e:
Exception = None so both annotations use Optional[Exception].
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd7c4a and c945542.

📒 Files selected for processing (2)
  • .github/workflows/test_api.yml
  • src/jmcomic/jm_config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/test_api.yml

@hect0x7 hect0x7 changed the title Dev Refactor logging infrastructure, enhance contributing documentation, and modernize Python version requirements to 3.9-3.14 Feb 23, 2026
@hect0x7
Copy link
Owner Author

hect0x7 commented Feb 23, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

♻️ Duplicate comments (1)
src/jmcomic/jm_config.py (1)

16-24: AI summary claims TopicFilter was introduced — it's not present in the code.

The enriched summary states "Introduced module-level logger and TopicFilter," but no TopicFilter (or any equivalent logging.Filter) was added to setup_default_jm_logger. The formatter at Line 21 still uses %(topic)s with no fallback. Any direct call to jm_logger.info/error() without extra={'topic': ...} will produce a KeyError silently swallowed by Handler.handleError() and written to stderr — as originally flagged in the previous open review comment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, The formatter in
setup_default_jm_logger references %(topic)s but no TopicFilter or equivalent is
added, causing KeyError when log calls omit extra['topic']; update
setup_default_jm_logger to attach a logging.Filter (e.g., TopicFilter) to
jm_logger or the handler that ensures a default 'topic' field (like empty string
or "default") is present on the LogRecord if missing, and register that filter
before adding the handler so jm_logger and its handler never emit records
lacking 'topic'.
🧹 Nitpick comments (2)
src/jmcomic/jm_config.py (2)

408-413: try/except TypeError is too broad — masks internal TypeErrors in EXECUTOR_LOG.

If EXECUTOR_LOG(topic, msg, e) raises a TypeError from within its own body (not from arity mismatch), the except block silently swallows it and re-invokes EXECUTOR_LOG(topic, msg), causing double-invocation and hiding the real bug. The previous review comment proposed an inspect.signature approach that targets arity specifically; the current implementation chose the blunt try/except instead.

Consider lazily checking arity once and caching the result instead of wrapping every call in a broad try/except:

♻️ Proposed refactor — lazy arity check
+    _executor_log_argc: int = None  # cached arity of EXECUTOR_LOG
+
     `@classmethod`
     def jm_log(cls, topic: str, msg: str, e: Exception = None):
         if cls.FLAG_ENABLE_JM_LOG is True:
-            try:
-                cls.EXECUTOR_LOG(topic, msg, e)
-            except TypeError:
-                cls.EXECUTOR_LOG(topic, msg)
+            if cls._executor_log_argc is None:
+                import inspect
+                try:
+                    cls._executor_log_argc = len(inspect.signature(cls.EXECUTOR_LOG).parameters)
+                except (ValueError, TypeError):
+                    cls._executor_log_argc = 3
+            if cls._executor_log_argc >= 3:
+                cls.EXECUTOR_LOG(topic, msg, e)
+            else:
+                cls.EXECUTOR_LOG(topic, msg)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 408 - 413, jm_log currently uses a
broad try/except around calling EXECUTOR_LOG(topic, msg, e) which can mask real
TypeError inside EXECUTOR_LOG and cause double-invocation; change jm_log to
determine once (lazily) whether EXECUTOR_LOG accepts three parameters by using
inspect.signature or callable introspection on JMConfig.EXECUTOR_LOG, cache that
boolean (e.g., _EXECUTOR_LOG_ACCEPTS_EXCEPTION) on the class, and then call
either EXECUTOR_LOG(topic, msg, e) or EXECUTOR_LOG(topic, msg) based on the
cached arity; keep honoring FLAG_ENABLE_JM_LOG and ensure the cached check runs
only once to avoid per-call overhead.

27-27: Use Optional[Exception] instead of implicit Optional on both e parameters.

Both default_jm_logging (Line 27) and JmModuleConfig.jm_log (Line 408) use e: Exception = None, which Ruff (RUF013) and PEP 484 flag as an implicit Optional.

♻️ Proposed fix
-def default_jm_logging(topic: str, msg, e: Exception = None):
+def default_jm_logging(topic: str, msg, e: Optional[Exception] = None):
-    def jm_log(cls, topic: str, msg: str, e: Exception = None):
+    def jm_log(cls, topic: str, msg: str, e: Optional[Exception] = None):

Add from typing import Optional at the top of the file (or use Exception | None for Python 3.10+ style).

Also applies to: 408-408

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` at line 27, Update the implicit optional Exception
annotations to explicit ones: add an import for Optional from typing (or use the
Python 3.10 union syntax Exception | None) and change the function signature for
default_jm_logging (the parameter e) and the jm_log member in JmModuleConfig to
use Optional[Exception] (or Exception | None) instead of e: Exception = None so
the types are explicit and satisfy Ruff/PEP484.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/jmcomic/jm_config.py`:
- Around line 16-24: The formatter in setup_default_jm_logger references
%(topic)s but no TopicFilter or equivalent is added, causing KeyError when log
calls omit extra['topic']; update setup_default_jm_logger to attach a
logging.Filter (e.g., TopicFilter) to jm_logger or the handler that ensures a
default 'topic' field (like empty string or "default") is present on the
LogRecord if missing, and register that filter before adding the handler so
jm_logger and its handler never emit records lacking 'topic'.

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Around line 408-413: jm_log currently uses a broad try/except around calling
EXECUTOR_LOG(topic, msg, e) which can mask real TypeError inside EXECUTOR_LOG
and cause double-invocation; change jm_log to determine once (lazily) whether
EXECUTOR_LOG accepts three parameters by using inspect.signature or callable
introspection on JMConfig.EXECUTOR_LOG, cache that boolean (e.g.,
_EXECUTOR_LOG_ACCEPTS_EXCEPTION) on the class, and then call either
EXECUTOR_LOG(topic, msg, e) or EXECUTOR_LOG(topic, msg) based on the cached
arity; keep honoring FLAG_ENABLE_JM_LOG and ensure the cached check runs only
once to avoid per-call overhead.
- Line 27: Update the implicit optional Exception annotations to explicit ones:
add an import for Optional from typing (or use the Python 3.10 union syntax
Exception | None) and change the function signature for default_jm_logging (the
parameter e) and the jm_log member in JmModuleConfig to use Optional[Exception]
(or Exception | None) instead of e: Exception = None so the types are explicit
and satisfy Ruff/PEP484.
ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd7c4a and c945542.

📒 Files selected for processing (2)
  • .github/workflows/test_api.yml
  • src/jmcomic/jm_config.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/test_api.yml

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.

🧹 Nitpick comments (3)
.github/workflows/test_api.yml (1)

19-19: Python 3.14 addition is correct; consider plugging the 3.11/3.12 gap in the matrix.

Python 3.14 was released on October 7, 2025 and is the latest stable release, so no allow-prereleases: true flag is needed — that part is correct.

The inline comment documenting 3.9's intentional retention addresses the previous review.

However, the updated matrix skips Python 3.11 and 3.12 entirely. Both are still within their active or security support windows (3.11 until October 2027, 3.12 until October 2028) and represent a large portion of the real-world install base. A regression introduced between 3.10 and 3.13 could go undetected in CI.

🔧 Suggested matrix to plug the gap
-        python-version: ['3.9', '3.10', '3.13', '3.14'] # 3.9 is kept as the minimum supported version test (EOL)
+        python-version: ['3.9', '3.10', '3.12', '3.13', '3.14'] # 3.9 is kept as the minimum supported version test (EOL)

Adding 3.12 (as a representative mid-range version) keeps the matrix lean while covering the 3.10–3.13 gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/test_api.yml at line 19, The matrix in the CI workflow
omits Python 3.11/3.12, leaving a gap between 3.10 and 3.13; update the
python-version entry for the test_api workflow (the "python-version" matrix key)
to include a mid-range release (e.g., add '3.12' or both '3.11' and '3.12') so
CI covers versions between 3.10 and 3.13 while keeping the matrix lean.
.github/CONTRIBUTING.md (1)

114-119: jm_logger is undeclared in the bad-example snippet.

The import block above (line 112) only brings in jm_log, so the contrasting anti-pattern using jm_logger.info(...) appears to reference an object that has never been obtained. A reader may wonder whether jm_logger is an importable symbol from jmcomic. A one-line context comment (e.g., # jm_logger = logging.getLogger('jmcomic')) would make the anti-pattern immediately self-explanatory.

📝 Proposed clarification
-# ❌ 避免直接调用底层 logger
-jm_logger.info("【api】请求成功")
+# ❌ 避免直接调用底层 logger(jm_logger 是 jmcomic 内部的原始 logging.Logger 实例)
+jm_logger.info("【api】请求成功")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/CONTRIBUTING.md around lines 114 - 119, The bad-example uses an
undeclared symbol jm_logger which confuses readers; update the snippet to either
declare or clarify jm_logger by adding a one-line context comment explaining how
it would be obtained (e.g., that jm_logger would be created via the standard
logging API) or replace the bad-example with an explicitly declared logger, and
keep the recommended jm_log('api', ...) example for contrast; reference the
symbols jm_logger and jm_log so reviewers can locate and fix the snippet.
src/jmcomic/jm_config.py (1)

27-27: Optional[Exception] annotation preferred over implicit Optional

PEP 484 prohibits e: Exception = None; use Optional[Exception] explicitly (Ruff RUF013). Same applies to line 408.

🔧 Proposed fix
-def default_jm_logging(topic: str, msg, e: Exception = None):
+def default_jm_logging(topic: str, msg, e: Optional[Exception] = None):
+from typing import Optional
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` at line 27, The type annotation for the exception
parameter in default_jm_logging should be explicit per PEP 484: change the
signature from e: Exception = None to e: Optional[Exception] = None and add
"from typing import Optional" at the top of the module; apply the same change to
the other function referenced around line 408 (update its exception parameter to
Optional[Exception] = None and ensure Optional is imported so both annotations
are valid).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/CONTRIBUTING.md:
- Around line 114-119: The bad-example uses an undeclared symbol jm_logger which
confuses readers; update the snippet to either declare or clarify jm_logger by
adding a one-line context comment explaining how it would be obtained (e.g.,
that jm_logger would be created via the standard logging API) or replace the
bad-example with an explicitly declared logger, and keep the recommended
jm_log('api', ...) example for contrast; reference the symbols jm_logger and
jm_log so reviewers can locate and fix the snippet.

In @.github/workflows/test_api.yml:
- Line 19: The matrix in the CI workflow omits Python 3.11/3.12, leaving a gap
between 3.10 and 3.13; update the python-version entry for the test_api workflow
(the "python-version" matrix key) to include a mid-range release (e.g., add
'3.12' or both '3.11' and '3.12') so CI covers versions between 3.10 and 3.13
while keeping the matrix lean.

In `@src/jmcomic/jm_config.py`:
- Line 27: The type annotation for the exception parameter in default_jm_logging
should be explicit per PEP 484: change the signature from e: Exception = None to
e: Optional[Exception] = None and add "from typing import Optional" at the top
of the module; apply the same change to the other function referenced around
line 408 (update its exception parameter to Optional[Exception] = None and
ensure Optional is imported so both annotations are valid).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bd7c4a and 103a7b3.

📒 Files selected for processing (7)
  • .github/CONTRIBUTING.md
  • .github/workflows/test_api.yml
  • README.md
  • assets/readme/README-en.md
  • assets/readme/README-jp.md
  • assets/readme/README-kr.md
  • src/jmcomic/jm_config.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • assets/readme/README-kr.md
  • README.md
  • assets/readme/README-jp.md
  • assets/readme/README-en.md

@hect0x7 hect0x7 changed the title Refactor logging infrastructure, enhance contributing documentation, and modernize Python version requirements to 3.9-3.14 v2.6.15: 日志基于logging重构; README更新Python版本要求 (>=3.9); 新增contributing.md Feb 23, 2026
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.

♻️ Duplicate comments (1)
.github/CONTRIBUTING.md (1)

38-38: Broken anchor — MD051 still flagged by markdownlint.

The static analysis tool continues to flag this link fragment as invalid. The author has self-tested and confirmed it works on GitHub, so no change is required if that holds.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/CONTRIBUTING.md at line 38, MD051 is flagging the link fragment for
"🛠️ 引擎改装规范(Coding Guidelines)" as broken; verify and fix the anchor so
markdownlint stops complaining by making the link fragment exactly match the
generated header id or by adding an explicit HTML id on the target header.
Specifically check the header text "🛠️ 引擎改装规范(Coding Guidelines)" and either
update the link fragment "#%EF%B8%8F-引擎改装规范-coding-guidelines" to the exact slug
produced by your Markdown renderer (encode emojis/parentheses appropriately) or
add an explicit id attribute to that header (e.g., <h2
id="engine-coding-guidelines">) and point the link to that id; confirm it still
renders correctly on GitHub and that markdownlint no longer flags MD051.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/CONTRIBUTING.md:
- Line 38: MD051 is flagging the link fragment for "🛠️ 引擎改装规范(Coding
Guidelines)" as broken; verify and fix the anchor so markdownlint stops
complaining by making the link fragment exactly match the generated header id or
by adding an explicit HTML id on the target header. Specifically check the
header text "🛠️ 引擎改装规范(Coding Guidelines)" and either update the link fragment
"#%EF%B8%8F-引擎改装规范-coding-guidelines" to the exact slug produced by your
Markdown renderer (encode emojis/parentheses appropriately) or add an explicit
id attribute to that header (e.g., <h2 id="engine-coding-guidelines">) and point
the link to that id; confirm it still renders correctly on GitHub and that
markdownlint no longer flags MD051.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 103a7b3 and f4a64b8.

📒 Files selected for processing (1)
  • .github/CONTRIBUTING.md

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

♻️ Duplicate comments (1)
src/jmcomic/jm_config.py (1)

16-24: ⚠️ Potential issue | 🟡 Minor

%(topic)s in formatter still has no fallback — direct jm_logger calls without extra will silently error.

The format string at line 21 references %(topic)s, but neither a defaults mapping nor a logging.Filter is present to supply it. Any code that calls jm_logger.info/debug/warning() directly (without extra={'topic': ...}) triggers a KeyError caught by Handler.handleError(), which writes a traceback to stderr. This was identified in prior reviews; the defaults={'topic': ''} fix was reverted for Python 3.9 compat, but the recommended filter-based alternative was not applied.

A logging.Filter on jm_logger works on Python 3.9+:

🛡️ Proposed fix — inject a default topic via Filter (Python 3.9 compatible)
 def setup_default_jm_logger():
     # 为了保持原有默认向下兼容,如果没有 handler,我们加一个控制台 handler
     if not jm_logger.handlers:
         import sys
         handler = logging.StreamHandler(sys.stdout)
         formatter = logging.Formatter('[%(asctime)s] [%(threadName)s]:【%(topic)s】%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
         handler.setFormatter(formatter)
         jm_logger.addHandler(handler)
         jm_logger.setLevel(logging.INFO)
+
+    class _DefaultTopic(logging.Filter):
+        def filter(self, record):
+            if not hasattr(record, 'topic'):
+                record.topic = ''
+            return True
+
+    if not any(isinstance(f, _DefaultTopic) for f in jm_logger.filters):
+        jm_logger.addFilter(_DefaultTopic())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, The formatter references
%(topic)s but no default is provided, causing KeyError when callers log without
extra; inside setup_default_jm_logger add and attach a logging.Filter (e.g., a
small class or lambda-style filter) to jm_logger or the handler that ensures
record.topic exists (set to empty string or a sensible default) before
formatting, then keep the existing formatter/handler setup and
jm_logger.setLevel call so direct jm_logger.info/debug/warning() calls no longer
raise via Handler.handleError().
🧹 Nitpick comments (2)
src/jmcomic/jm_config.py (2)

27-27: PEP 484 prohibits implicit Optional — applies to both default_jm_logging (line 27) and jm_log (line 408).

Both e: Exception = None signatures should use an explicit Optional:

🔧 Suggested fix
+from typing import Optional
+
-def default_jm_logging(topic: str, msg, e: Exception = None):
+def default_jm_logging(topic: str, msg, e: Optional[Exception] = None):
-    def jm_log(cls, topic: str, msg: str, e: Exception = None):
+    def jm_log(cls, topic: str, msg: str, e: Optional[Exception] = None):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` at line 27, Update the type annotations for the
optional exception parameters to use typing.Optional instead of implicit None:
change the signatures of default_jm_logging and jm_log to accept e:
Optional[Exception] = None, add "from typing import Optional" at the top of the
module (or include Optional in the existing typing import), and run a quick
type-check to ensure no other annotations rely on implicit Optional usage.

16-24: Library adds a StreamHandler at import time — duplicate output when the application configures the root logger.

jm_logger.propagate defaults to True. If the application calls logging.basicConfig() or adds any root-logger handler after (or before) importing jmcomic, every jmcomic log record is emitted twice: once through jm_logger's own StreamHandler and once through propagation to the root logger. The Python logging documentation recommends library authors use NullHandler to avoid interfering with the application's logging configuration.

Since this is intentionally for backward compatibility, a lighter mitigation is to set jm_logger.propagate = False inside setup_default_jm_logger when the default handler is added — preventing root-logger duplication while keeping the backward-compat output path intact.

💡 Suggested mitigation
     if not jm_logger.handlers:
         import sys
         handler = logging.StreamHandler(sys.stdout)
         formatter = logging.Formatter(...)
         handler.setFormatter(formatter)
         jm_logger.addHandler(handler)
         jm_logger.setLevel(logging.INFO)
+        jm_logger.propagate = False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, The setup_default_jm_logger
function currently adds a StreamHandler which causes duplicate logs via
propagation; modify setup_default_jm_logger so that when you add the default
StreamHandler to jm_logger you also set jm_logger.propagate = False (only in the
branch that adds the handler) to prevent records from also being handled by the
root logger while preserving the existing console output fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/jmcomic/jm_config.py`:
- Around line 408-426: The jm_log exception path currently calls
inspect.signature(executor) on every error and emits a warnings.warn without
stacklevel; change jm_log to cache the executor arity (e.g., a class-level dict
keyed by id(EXECUTOR_LOG) or executor object to store params_count) so you only
compute inspect.signature once per executor identity, and when emitting the
deprecation/warning call inside jm_log include stacklevel=2 in warnings.warn so
the warning points to the caller site; update code references jm_log,
EXECUTOR_LOG and FLAG_ENABLE_JM_LOG to use the cached arity and add stacklevel=2
to the warning invocation.

---

Duplicate comments:
In `@src/jmcomic/jm_config.py`:
- Around line 16-24: The formatter references %(topic)s but no default is
provided, causing KeyError when callers log without extra; inside
setup_default_jm_logger add and attach a logging.Filter (e.g., a small class or
lambda-style filter) to jm_logger or the handler that ensures record.topic
exists (set to empty string or a sensible default) before formatting, then keep
the existing formatter/handler setup and jm_logger.setLevel call so direct
jm_logger.info/debug/warning() calls no longer raise via Handler.handleError().

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Line 27: Update the type annotations for the optional exception parameters to
use typing.Optional instead of implicit None: change the signatures of
default_jm_logging and jm_log to accept e: Optional[Exception] = None, add "from
typing import Optional" at the top of the module (or include Optional in the
existing typing import), and run a quick type-check to ensure no other
annotations rely on implicit Optional usage.
- Around line 16-24: The setup_default_jm_logger function currently adds a
StreamHandler which causes duplicate logs via propagation; modify
setup_default_jm_logger so that when you add the default StreamHandler to
jm_logger you also set jm_logger.propagate = False (only in the branch that adds
the handler) to prevent records from also being handled by the root logger while
preserving the existing console output fallback behavior.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4a64b8 and ad692a1.

📒 Files selected for processing (1)
  • src/jmcomic/jm_config.py

Comment on lines +408 to +426
def jm_log(cls, topic: str, msg: str, e: Exception = None):
if cls.FLAG_ENABLE_JM_LOG is True:
cls.EXECUTOR_LOG(topic, msg)
executor = cls.EXECUTOR_LOG
if e is None:
executor(topic, msg)
else:
import inspect
try:
sig = inspect.signature(executor)
params_count = len(sig.parameters)
except (ValueError, TypeError):
params_count = 2

if params_count >= 3:
executor(topic, msg, e)
else:
import warnings
warnings.warn('jmcomic已升级到标准logging,建议将EXECUTOR_LOG重新定义为3个参数以接收异常对象 (topic, msg, e)')
executor(topic, msg)
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

warnings.warn missing stacklevel + repeated inspect.signature reflection on every error call.

Two issues in the exception dispatch path:

  1. stacklevel absent (Ruff B028, line 425): Without stacklevel=2, the warning's source location points inside jm_log rather than the caller site, making it hard for users to identify which code needs updating.

  2. Uncached arity detection (lines 414–419): inspect.signature(executor) is invoked on every jm_log call that includes an exception. EXECUTOR_LOG rarely changes at runtime, so caching the result (keyed by executor identity) avoids repeated reflection overhead in error-heavy paths.

✨ Proposed fix
+    _executor_arity_cache: dict = {}
+
     `@classmethod`
     def jm_log(cls, topic: str, msg: str, e: Exception = None):
         if cls.FLAG_ENABLE_JM_LOG is True:
             executor = cls.EXECUTOR_LOG
             if e is None:
                 executor(topic, msg)
             else:
-                import inspect
-                try:
-                    sig = inspect.signature(executor)
-                    params_count = len(sig.parameters)
-                except (ValueError, TypeError):
-                    params_count = 2
+                cache = cls._executor_arity_cache
+                if executor not in cache:
+                    import inspect
+                    try:
+                        cache[executor] = len(inspect.signature(executor).parameters)
+                    except (ValueError, TypeError):
+                        cache[executor] = 2
+                params_count = cache[executor]
 
                 if params_count >= 3:
                     executor(topic, msg, e)
                 else:
                     import warnings
-                    warnings.warn('jmcomic已升级到标准logging,建议将EXECUTOR_LOG重新定义为3个参数以接收异常对象 (topic, msg, e)')
+                    warnings.warn(
+                        'jmcomic已升级到标准logging,建议将EXECUTOR_LOG重新定义为3个参数以接收异常对象 (topic, msg, e)',
+                        stacklevel=2,
+                    )
                     executor(topic, msg)
🧰 Tools
🪛 Ruff (0.15.1)

[warning] 408-408: PEP 484 prohibits implicit Optional

Convert to Optional[T]

(RUF013)


[warning] 425-425: No explicit stacklevel keyword argument found

Set stacklevel=2

(B028)


[warning] 425-425: String contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 408 - 426, The jm_log exception path
currently calls inspect.signature(executor) on every error and emits a
warnings.warn without stacklevel; change jm_log to cache the executor arity
(e.g., a class-level dict keyed by id(EXECUTOR_LOG) or executor object to store
params_count) so you only compute inspect.signature once per executor identity,
and when emitting the deprecation/warning call inside jm_log include
stacklevel=2 in warnings.warn so the warning points to the caller site; update
code references jm_log, EXECUTOR_LOG and FLAG_ENABLE_JM_LOG to use the cached
arity and add stacklevel=2 to the warning invocation.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/docs/sources/tutorial/11_log_custom.md`:
- Around line 11-18: Split the combined example into two separate, clearly
labeled code blocks so readers understand they are alternatives: create one
block titled "方式 A(推荐):使用 Python 原生 logging 控制" showing use of
logging.getLogger("jmcomic").setLevel(logging.WARNING), and a second block
titled "方式 B:使用 disable_jm_log() 完全关闭日志" showing from jmcomic import
disable_jm_log and disable_jm_log(). Ensure the text explains they are mutually
exclusive alternatives and remove the single sequential snippet that mixes both.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad692a1 and 4e00a32.

📒 Files selected for processing (2)
  • assets/docs/sources/tutorial/11_log_custom.md
  • assets/docs/sources/tutorial/4_module_custom.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • assets/docs/sources/tutorial/4_module_custom.md

Comment on lines 11 to 18
```python
from jmcomic import disable_jm_log
disable_jm_log()

# 推荐使用 Python 原生的 logging 控制
import logging
logging.getLogger("jmcomic").setLevel(logging.WARNING) # 只显示警告以上的日志
```
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

Split the two alternative approaches into separate, clearly labelled code blocks.

disable_jm_log() and logging.getLogger(...).setLevel(...) are mutually exclusive alternatives, but they are presented as a single sequential snippet. A reader who copies the block calls disable_jm_log() first (which disables all output) and then calls setLevel(logging.WARNING) — the outcome depends on how disable_jm_log is implemented and is at best confusing, at worst a silent no-op for the second call. The inline comment "推荐使用 Python 原生的 logging 控制" already signals that the second snippet is preferred, so presenting both in one block contradicts that recommendation.

📝 Proposed fix: separate into two distinct blocks
-```python
-from jmcomic import disable_jm_log 
-disable_jm_log()
-
-# 推荐使用 Python 原生的 logging 控制
-import logging
-logging.getLogger("jmcomic").setLevel(logging.WARNING) # 只显示警告以上的日志
-```
+**方式 A(推荐):使用 Python 原生 logging 控制**
+
+```python
+import logging
+logging.getLogger("jmcomic").setLevel(logging.WARNING)  # 只显示警告以上的日志
+```
+
+**方式 B:使用 `disable_jm_log()` 完全关闭日志**
+
+```python
+from jmcomic import disable_jm_log
+disable_jm_log()
+```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@assets/docs/sources/tutorial/11_log_custom.md` around lines 11 - 18, Split
the combined example into two separate, clearly labeled code blocks so readers
understand they are alternatives: create one block titled "方式 A(推荐):使用 Python 原生
logging 控制" showing use of
logging.getLogger("jmcomic").setLevel(logging.WARNING), and a second block
titled "方式 B:使用 disable_jm_log() 完全关闭日志" showing from jmcomic import
disable_jm_log and disable_jm_log(). Ensure the text explains they are mutually
exclusive alternatives and remove the single sequential snippet that mixes both.

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

♻️ Duplicate comments (2)
src/jmcomic/jm_config.py (2)

16-24: %(topic)s in formatter still lacks a fallback after defaults revert — direct jm_logger calls will error.

The defaults kwarg was correctly reverted for Python 3.9 compatibility, but the Filter-based alternative from that same review was never applied. Any direct jm_logger.info("msg") call (without extra={'topic': ...}) hits a KeyError in the formatter, which Handler.handleError catches and dumps to stderr.

Add a logging.Filter to inject a default topic attribute:

Proposed fix
 def setup_default_jm_logger():
     # 为了保持原有默认向下兼容,如果没有 handler,我们加一个控制台 handler
     if not jm_logger.handlers:
         import sys
         handler = logging.StreamHandler(sys.stdout)
         formatter = logging.Formatter('[%(asctime)s] [%(threadName)s]:【%(topic)s】%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
         handler.setFormatter(formatter)
         jm_logger.addHandler(handler)
         jm_logger.setLevel(logging.INFO)
+
+    # Inject default 'topic' for records that omit extra={'topic': ...} (Python 3.9-safe)
+    class _TopicDefault(logging.Filter):
+        def filter(self, record):
+            if not hasattr(record, 'topic'):
+                record.topic = ''
+            return True
+
+    jm_logger.addFilter(_TopicDefault())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 16 - 24, The formatter references
record.topic but direct jm_logger calls without extra={'topic':...} raise
KeyError; update setup_default_jm_logger to attach a logging.Filter (e.g.,
subclass Filter with filter(self, record): if not hasattr(record,'topic'):
record.topic='JM') to the handler or jm_logger so a default topic is injected
before formatting; locate setup_default_jm_logger and jm_logger and add/apply
that Filter to prevent KeyError when extra is missing.

408-429: Backward-compat dispatch logic is correct; stacklevel=2 addressed.

The arity detection via inspect.signature correctly handles plain functions, lambdas, bound methods, and built-ins (via the except fallback). The stacklevel=2 addition ensures the warning points to the caller site.

The uncached inspect.signature call on every exception-bearing jm_log invocation was flagged in a prior review. It's a minor performance concern on error-heavy paths — consider caching keyed on id(executor) if this becomes hot.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 408 - 429, The jm_log function
repeatedly calls inspect.signature(executor) whenever an exception is logged,
which can be expensive; cache the arity detection keyed by id(executor) (e.g., a
module-level dict mapping executor id to parameter count) and use that cached
params_count instead of recomputing on each invocation; update jm_log (and any
helper that computes params_count) to consult the cache, populate it in the
except/try flow when needed, and keep the existing fallback and warning behavior
intact; refer to jm_log, EXECUTOR_LOG and FLAG_ENABLE_JM_LOG to locate where to
add the cache.
🧹 Nitpick comments (1)
src/jmcomic/jm_config.py (1)

27-36: Solid shorthand support and clean logging dispatch.

The isinstance(msg, BaseException) check for jm_log('topic', e) shorthand is a nice convenience. Logging with exc_info=e for exceptions is correct.

Nit: Ruff RUF013 flags e: Exception = None — PEP 484 prefers Optional[Exception] (or Exception | None for ≥3.10). Same applies to jm_log at Line 408.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 27 - 36, Update the function
signatures to use an explicit optional type for the exception parameter (e.g.,
change e: Exception = None to e: Optional[Exception] or Exception | None) in
both default_jm_logging and the jm_log function to satisfy PEP 484/ruff RUF013;
add the necessary typing import (from typing import Optional) if using Optional,
and ensure any type hints and docstrings are adjusted accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/jmcomic/jm_config.py`:
- Around line 410-429: The LogTopicFilterPlugin is replacing EXECUTOR_LOG with
new_jm_log(topic, msg) which drops exceptions; change
LogTopicFilterPlugin.new_jm_log to accept an optional third parameter (e=None)
and forward it to the previous executor (call old_jm_log(topic, msg, e)) so
exceptions aren’t silently discarded; update any closures that wrap jm_log/_log
to use a (topic, msg, e=None) signature and ensure callers of EXECUTOR_LOG
continue to work with 3-arg executors while still supporting legacy 2-arg
handlers if needed.

---

Duplicate comments:
In `@src/jmcomic/jm_config.py`:
- Around line 16-24: The formatter references record.topic but direct jm_logger
calls without extra={'topic':...} raise KeyError; update setup_default_jm_logger
to attach a logging.Filter (e.g., subclass Filter with filter(self, record): if
not hasattr(record,'topic'): record.topic='JM') to the handler or jm_logger so a
default topic is injected before formatting; locate setup_default_jm_logger and
jm_logger and add/apply that Filter to prevent KeyError when extra is missing.
- Around line 408-429: The jm_log function repeatedly calls
inspect.signature(executor) whenever an exception is logged, which can be
expensive; cache the arity detection keyed by id(executor) (e.g., a module-level
dict mapping executor id to parameter count) and use that cached params_count
instead of recomputing on each invocation; update jm_log (and any helper that
computes params_count) to consult the cache, populate it in the except/try flow
when needed, and keep the existing fallback and warning behavior intact; refer
to jm_log, EXECUTOR_LOG and FLAG_ENABLE_JM_LOG to locate where to add the cache.

---

Nitpick comments:
In `@src/jmcomic/jm_config.py`:
- Around line 27-36: Update the function signatures to use an explicit optional
type for the exception parameter (e.g., change e: Exception = None to e:
Optional[Exception] or Exception | None) in both default_jm_logging and the
jm_log function to satisfy PEP 484/ruff RUF013; add the necessary typing import
(from typing import Optional) if using Optional, and ensure any type hints and
docstrings are adjusted accordingly.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e00a32 and 41b07d3.

📒 Files selected for processing (1)
  • src/jmcomic/jm_config.py

Comment on lines +410 to +429
executor = cls.EXECUTOR_LOG
if e is None:
executor(topic, msg)
else:
import inspect
try:
sig = inspect.signature(executor)
params_count = len(sig.parameters)
except (ValueError, TypeError):
params_count = 2

if params_count >= 3:
executor(topic, msg, e)
else:
import warnings
warnings.warn(
'jmcomic已升级到标准logging,建议将EXECUTOR_LOG重新定义为3个参数以接收异常对象 (topic, msg, e)',
stacklevel=2
)
executor(topic, msg)
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

LogTopicFilterPlugin will silently drop exceptions — consider updating it.

The LogTopicFilterPlugin (in jm_plugin.py:547-562) defines new_jm_log(topic, msg) with only 2 parameters and replaces EXECUTOR_LOG. When jm_log is called with an exception, the arity check here will detect 2 params, emit the deprecation warning, and call with (topic, msg) — silently discarding the exception object even though the underlying executor (old_jm_log) supports 3 params.

Consider updating LogTopicFilterPlugin.new_jm_log to (topic, msg, e=None) and forwarding e to old_jm_log.

#!/bin/bash
# Verify all EXECUTOR_LOG replacements in the codebase that use 2-param signatures
rg -n "EXECUTOR_LOG\s*=" --type=py -B1 -A5
echo "---"
# Also check for any closures wrapping the log executor
rg -n "def.*jm_log\|def.*_log" --type=py -B1 -A3 | grep -A4 "def "
🧰 Tools
🪛 Ruff (0.15.1)

[warning] 426-426: String contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF001)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/jmcomic/jm_config.py` around lines 410 - 429, The LogTopicFilterPlugin is
replacing EXECUTOR_LOG with new_jm_log(topic, msg) which drops exceptions;
change LogTopicFilterPlugin.new_jm_log to accept an optional third parameter
(e=None) and forward it to the previous executor (call old_jm_log(topic, msg,
e)) so exceptions aren’t silently discarded; update any closures that wrap
jm_log/_log to use a (topic, msg, e=None) signature and ensure callers of
EXECUTOR_LOG continue to work with 3-arg executors while still supporting legacy
2-arg handlers if needed.

@hect0x7 hect0x7 merged commit 39b1a2e into master Feb 23, 2026
1 check passed
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