Skip to content

fix(files): never mutate the shared PROPFIND property lists - #455

Merged
oleksandr-nc merged 2 commits into
mainfrom
fix/propfind-properties-mutation
Aug 3, 2026
Merged

fix(files): never mutate the shared PROPFIND property lists#455
oleksandr-nc merged 2 commits into
mainfrom
fix/propfind-properties-mutation

Conversation

@oleksandr-nc

@oleksandr-nc oleksandr-nc commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

get_propfind_properties() and both trashbin_list() implementations extended the shared PROPFIND_PROPERTIES constant with += instead of copying it, so it grew on every call: +7 entries when the server advertises files.locking, +3 per trashbin_list(). Long-running clients ended up sending multi-megabyte PROPFIND bodies (measured: ~138 bytes of growth per listdir() call), and the server cost is O(properties x resources).

  • build a fresh list in all three places
  • both constants are now Final[tuple[str, ...]], so in-place mutation cannot come back
  • Sequence[str] where those lists are only iterated
  • 7 regression tests in tests_unit/, verified to fail on main

Closes #453

Summary by CodeRabbit

  • Bug Fixes

    • Fixed repeated file and directory listing requests expanding over time.
    • Prevented shared property settings from being modified during trash-bin and locking-property operations.
    • Improved request consistency for synchronous and asynchronous listings.
  • Tests

    • Added coverage confirming property settings remain unchanged, fresh property lists are returned, and trash-bin requests use the correct properties.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@oleksandr-nc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 24 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f4f3189-1124-40ab-b3c8-54f00f49efe8

📥 Commits

Reviewing files that changed from the base of the PR and between e03c69a and 6104c72.

📒 Files selected for processing (1)
  • tests_unit/test_propfind_properties.py
📝 Walkthrough

Walkthrough

The change prevents shared PROPFIND property collections from growing across calls. It uses immutable typed constants, fresh lists for extensions, sequence-based annotations, and regression tests for synchronous and asynchronous TrashBin requests.

Changes

PROPFIND property handling

Layer / File(s) Summary
Immutable property contracts
nc_py_api/files/_files.py, tests_unit/test_propfind_properties.py
PROPFIND constants are typed immutable tuples. get_propfind_properties() returns a fresh list. Directory request and response builders accept string sequences. Tests verify immutability, fresh results, and capability-based locking properties.
TrashBin property requests
nc_py_api/files/files.py, nc_py_api/files/files_async.py, tests_unit/test_propfind_properties.py, CHANGELOG.md
Synchronous and asynchronous TrashBin listings construct independent property lists. Tests verify constant preservation, requested properties, and PropFindType.TRASHBIN. The changelog records the fix.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes fix all mutation paths in issue #453 and add regression tests for shared constants, capability branches, and trashbin requests.
Out of Scope Changes check ✅ Passed The changelog, typing updates, API parameter widening, implementation changes, and tests directly support the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing mutation of shared PROPFIND property lists.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/propfind-properties-mutation

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.

get_propfind_properties() aliased the module-level PROPFIND_PROPERTIES and
extended it with +=, and both trashbin_list() implementations did the same,
so the shared list grew by 7 entries per call on servers advertising
files.locking and by 3 per trashbin_list(). A long-running client ended up
sending multi-megabyte PROPFIND bodies, and the server cost is
O(properties x resources), so it could starve the Nextcloud instance it was
talking to.

Build a fresh list in each of the three places and turn both constants into
tuples, so any future in-place mutation fails immediately instead of
silently corrupting shared state.

Closes #453

Signed-off-by: Oleksandr Piskun <oleksandr2088@icloud.com>
@oleksandr-nc
oleksandr-nc force-pushed the fix/propfind-properties-mutation branch from b429b26 to e03c69a Compare August 3, 2026 13:44
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.75%. Comparing base (126a31e) to head (6104c72).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #455      +/-   ##
==========================================
+ Coverage   94.69%   94.75%   +0.06%     
==========================================
  Files          48       49       +1     
  Lines        5650     5724      +74     
==========================================
+ Hits         5350     5424      +74     
  Misses        300      300              
Files with missing lines Coverage Δ
nc_py_api/files/_files.py 96.56% <100.00%> (+0.02%) ⬆️
nc_py_api/files/files.py 96.15% <100.00%> (ø)
nc_py_api/files/files_async.py 96.13% <100.00%> (ø)
tests_unit/test_propfind_properties.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The mutation checks bound the constants by name instead of copying them, so
the comparison could never fail if the constants were ever lists again: on
the unfixed code that test passed while the list was being corrupted.

Snapshot both constants at import time and assert against those copies, and
pin that the helper still returns a list.

Signed-off-by: Oleksandr Piskun <oleksandr2088@icloud.com>
@oleksandr-nc
oleksandr-nc merged commit b014f93 into main Aug 3, 2026
13 checks passed
@oleksandr-nc
oleksandr-nc deleted the fix/propfind-properties-mutation branch August 3, 2026 14:36
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.

get_propfind_properties mutates PROPFIND_PROPERTIES in place: property list grows 7 entries per call, producing multi-MB PROPFIND bodies

1 participant