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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Concise, project-specific guidance for AI coding agents working on this repo. Fo
## 2. Key Behavioral Invariants
- DO NOT mutate caller inputs—copy/normalize (shallow copy for mappings; deep-copy only when a callable filter may mutate; index-projection for sequences) before traversal.
- Cycle detection in `encode._encode` must raise `ValueError("Circular reference detected")`—preserve side-channel algorithm.
- Depth, list, and parameter limits are security/safety features: respect `depth`, `max_depth`, `list_limit`, `parameter_limit`, and `strict_depth` / `raise_on_limit_exceeded` exactly as tests assert. `max_depth` is capped to the current recursion limit.
- Depth, list, and parameter limits are security/safety features: respect `depth`, `max_depth`, `list_limit`, `parameter_limit`, and `strict_depth` / `raise_on_limit_exceeded` exactly as tests assert. For encoding, `max_depth=None` means unbounded traversal (`sys.maxsize`); explicit values are enforced directly without recursion-limit capping, and exceeding an explicit `max_depth` raises `ValueError("Maximum encoding depth exceeded")`.
- Duplicate key handling delegated to `Duplicates` enum: COMBINE → list accumulation; FIRST/LAST semantics enforced during merge.
- List format semantics (`ListFormat` enum) change how prefixes are generated; COMMA + `comma_round_trip=True` must emit single-element marker for round-trip fidelity.
- Charset sentinel logic: when `charset_sentinel=True`, prepend sentinel *before* payload; obey override rules when both charset and sentinel present.
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
- Preserve or raise the coverage level tracked in `coverage.xml`; CI flags regressions.
- Name tests `test_{feature}_{scenario}` and refresh fixtures whenever query-string semantics shift.
- When touching cross-language behavior, run `tests/comparison/compare_outputs.sh` to confirm parity with the Node reference.
- For encoding depth changes, cover `EncodeOptions.max_depth` (positive int/None) and cap-to-recursion behavior.
- For encoding depth changes, cover `EncodeOptions.max_depth` (positive int/None): `None` means unbounded traversal
(`sys.maxsize`) and explicit values are enforced directly (no recursion-limit capping).

## Commit & Pull Request Guidelines
- Follow the emoji-prefixed summaries visible in `git log` (e.g., `:arrow_up: Bump actions/setup-python from 5 to 6 (#26)`), using the imperative mood.
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 1.4.1-wip

* [FIX] harden encoder traversal to an iterative implementation to avoid recursion-based crashes on very deep nested input
* [FIX] harden decode merge path (`Utils.merge`) with iterative traversal to prevent `RecursionError` on deep conflicting merges
* [FIX] update `EncodeOptions.max_depth` semantics: `None` is unbounded by this option; explicit limits are enforced directly
* [FIX] preserve legacy map-merge key collision semantics for mixed key types (`'1'` vs `1`) in iterative merge
* [CHORE] optimize deep encode performance by replacing per-frame side-channel chain scans with O(1) ancestry cycle state lookups
* [CHORE] add deep stack-safety regressions (depth 12_000) and cycle-state compatibility tests
* [CHORE] update encoding depth documentation

## 1.4.0

* [FEAT] add `EncodeOptions.max_depth` to cap encoding traversal depth (capped to the current recursion limit)
Expand Down
3 changes: 1 addition & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -463,8 +463,7 @@ Maximum encoding depth

You can cap how deep the encoder will traverse by setting the
`max_depth <https://techouse.github.io/qs_codec/qs_codec.models.html#qs_codec.models.encode_options.EncodeOptions.max_depth>`__
option. If unset, the encoder derives a safe limit from the interpreter recursion limit; when set, the effective
limit is capped to the current recursion limit to avoid ``RecursionError``.
option. If unset, traversal is unbounded by this option. When set, the provided limit is enforced directly.

.. code:: python

Expand Down
5 changes: 2 additions & 3 deletions docs/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,8 @@ Maximum encoding depth
^^^^^^^^^^^^^^^^^^^^^^

You can cap how deep the encoder will traverse by setting the
:py:attr:`max_depth <qs_codec.models.encode_options.EncodeOptions.max_depth>` option. If unset, the encoder derives a
safe limit from the interpreter recursion limit; when set, the effective limit is capped to the current recursion
limit to avoid ``RecursionError``.
:py:attr:`max_depth <qs_codec.models.encode_options.EncodeOptions.max_depth>` option.
If unset, traversal is unbounded by this option. When set, the provided limit is enforced directly.

.. code:: python

Expand Down
Loading