Skip to content

Fix _remove_circular_references nulling shared non-circular config values #1194

Description

@pamod-madubashana

Description

ConfigManager._remove_circular_references in pdf2zh/config.py:62 treats any object seen twice as a cycle. It threads a single shared seen set through the whole traversal (pdf2zh/config.py:64-69) and never removes an id when recursion unwinds, so a diamond (shared but non-circular) reference is wrongly replaced with None at pdf2zh/config.py:67-68. Since _save_config (pdf2zh/config.py:54-60) persists the cleaned data, saving a config containing the same dict/list object in two places silently drops the second occurrence (data loss), even though the data is perfectly JSON-serializable.

Reproduce

  1. From the repository root, run:
    python -c 'from pdf2zh.config import ConfigManager; m=ConfigManager.__new__(ConfigManager); shared={"a":1}; obj={"x":shared,"y":shared}; print(m._remove_circular_references(obj))'
    Observed result: {'x': {'a': 1}, 'y': None} — the second reference is nulled though there is no cycle.
  2. List case: python -c 'from pdf2zh.config import ConfigManager; m=ConfigManager.__new__(ConfigManager); s=[1,2]; print(m._remove_circular_references([s,s]))'
    Observed result: [[1, 2], None] — same wrongful nulling.
  3. Sanity check that true cycles are the intended target: a self-referential dict still collapses to None as expected, so the method conflates diamonds with cycles.

Expected

Only true ancestor-path cycles should collapse to None; shared references reachable via sibling branches must be preserved verbatim. Smallest fix: scope seen to the current recursion path in _remove_circular_references — add the object id before recursing into its children and discard it afterwards (or pass a copied/extended set down), instead of one never-shrunk global set.

Checklist

Searched open and closed issues and pull requests (circular, ConfigManager, save_config shared reference None, ZhipuTranslator/do_translate variants, AnythingLLM lang_in lang_out swapped, tencent chunk split MAX_CHARS) -- zero overlap. File:line + repro provided.

Distinct from #766 (#766, "Why circular dependency?"): that issue is about running modules in PyCharm / import setup, not about ConfigManager._remove_circular_references nulling shared config values on save.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions