Skip to content

RF: switch from unmaintained fusepy to mfusepy - #139

Merged
yarikoptic merged 3 commits into
masterfrom
claude/zen-pasteur-6bk8it
Oct 6, 2026
Merged

yarikoptic merged 3 commits into
masterfrom
claude/zen-pasteur-6bk8it

Conversation

@yarikoptic

Copy link
Copy Markdown
Member

Why. fusepy has had no release since 3.0.1 (2018) and still has the FUSE._wrapper bugs (fusepy/fusepy#124, fusepy/fusepy#146) that #136 works around with a runtime patch. mfusepy is the maintained fork:

  • used by ratarmount and NVIDIA's Megatron-Energon;
  • ISC license, on PyPI and conda-forge (3.1.1, 2026-03), not yet in Debian;
  • fixes both bugs: _wrapper is a regular method, and isinstance(e.errno, int) guards the errno check;
  • supports libfuse 3, preferring libfuse 2 when both are installed (FUSE_LIBRARY_NAME=fuse3 forces 3).

Not a drop-in swap. Since 3.0, mfusepy calls Operations methods directly instead of through Operations.__call__, which DataLadFUSE overrode to map FUSE paths into the dataset and hide .git. After a bare import mfusepy as fuse, the mount would expose the host's / instead of the dataset; the new test catches exactly that.

Changes

  • setup.cfg: mfusepy ~= 3.1 replaces fusepy; the fuse extra is dropped from fsspec, since it only pulled in fusepy.
  • fuse_.py: __call__ becomes __getattribute__, which wraps every path-taking Operations callback (the same approach as mfusepy's LoggingMixIn).
  • New test_callbacks_map_paths: an in-process test (no mount) of the path mapping, of .git hiding, and of the create() signature that mfusepy inspects.
  • Docs: installing fuse3 alone is now enough, so the per-release instructions for adding libfuse 2 are gone. The FUSE() example and the autodoc mock use mfusepy.
  • CI: the py314-libfuse row runs against libfuse 3 (fuse3 package plus FUSE_LIBRARY_NAME=fuse3, passed through tox). py314-full stays on libfuse 2.

Relation to #136. Once this is merged, #136's _fusepy_patch.py commit is no longer needed; its test stabilization stays useful. Whichever lands second needs a small conflict fix in fuse_.py's imports and in test.yml.

Tested locally (Ubuntu 24.04, Python 3.13, --libfuse --no-network):

  • libfuse 2.9 and libfuse 3.14 each give 104 passed, 21 skipped, 1 failed.
  • The failure is test_parallel_access, which fetches archive.org files that were unreachable from the test machine. Unmodified master with fusepy fails the same test there too, with fusepy's ERANGE symptom where mfusepy gives a clean EINVAL.
  • flake8, mypy and codespell are clean. The Sphinx build has no warnings except intersphinx, which also had no network.

Follow-up, not in this PR: DataLadFUSE.utimens calls os.utime(..., ns=times), but without use_ns = True it receives float seconds, so it has always failed. Switching to use_ns would also mean returning st_*time as nanoseconds; mfusepy 4 will make that mandatory.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NoaVLBBnhaZqmhrk813G2Z


Generated by Claude Code

Yaroslav Halchenko and others added 3 commits October 6, 2026 21:02
fusepy has had no release since 3.0.1 (2018) and still has the
FUSE._wrapper bugs (fusepy/fusepy#124, #146; worked around in #136): a
BaseException, or an OSError without errno such as TimeoutError, raised
by a callback makes the error handler itself fail with NameError, so the
error is lost and garbage is returned to libfuse.  mfusepy
(github.com/mxmlnkn/mfusepy, ISC, on PyPI and conda-forge) is the
maintained fork used by ratarmount: it fixes both bugs and adds libfuse 3
support, preferring libfuse 2 when both are installed.

mfusepy 3 is not a drop-in replacement: it calls the callbacks directly
instead of dispatching via Operations.__call__, which DataLadFUSE
overrode to map FUSE paths into the dataset and hide .git.  After a bare
import swap the mount would expose the host's / instead of the dataset.
Do that mapping in __getattribute__ instead (as mfusepy's LoggingMixIn
does) and cover it with an in-process test.

- depend on mfusepy ~= 3.1 instead of fusepy; drop fsspec's "fuse"
  extra, which only pulled in fusepy
- README: libfuse 2 or 3 will do, libfuse2t64 is no longer needed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoaVLBBnhaZqmhrk813G2Z
mfusepy works with libfuse 3 as well, so installing fuse3 is enough and
the per-release instructions for adding libfuse 2 are no longer needed.
Mention FUSE_LIBRARY_NAME=fuse3 for when both are installed, and use
mfusepy in the FUSE() example and for the autodoc mock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoaVLBBnhaZqmhrk813G2Z
mfusepy supports libfuse 3, so exercise it: on that row install fuse3
and set FUSE_LIBRARY_NAME=fuse3 (now passed through tox).  The
py314-full row keeps testing libfuse 2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoaVLBBnhaZqmhrk813G2Z
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 datalad-fuse | 🛠️ Build #34979627 | 📁 Comparing ebb484a against latest (83a0a30)

  🔍 Preview build  

6 files changed · ± 6 modified

± Modified

@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.55556% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.48%. Comparing base (83a0a30) to head (ebb484a).

Files with missing lines Patch % Lines
datalad_fuse/__init__.py 0.00% 1 Missing ⚠️
datalad_fuse/fuse_.py 94.44% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #139      +/-   ##
==========================================
- Coverage   92.84%   84.48%   -8.36%     
==========================================
  Files          13       14       +1     
  Lines        1188     1502     +314     
==========================================
+ Hits         1103     1269     +166     
- Misses         85      233     +148     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@yarikoptic
yarikoptic merged commit 05e75e4 into master Oct 6, 2026
16 of 17 checks passed
@yarikoptic
yarikoptic deleted the claude/zen-pasteur-6bk8it branch October 6, 2026 21:27
yarikoptic pushed a commit that referenced this pull request Oct 6, 2026
…n-br2ms9

Brings in the switch from fusepy to mfusepy (#139).

Conflicts:
- CONTRIBUTING.md, docs/source/installation.rst: take master's FUSE
  installation instructions (FUSE 3 alone suffices with mfusepy); keep
  this branch's other changes to the installation page
- docs/source/conf.py: keep this branch's autodoc defaults, with
  master's mfusepy mock

Also update the troubleshooting entry for failing reads in the mount:
with mfusepy, programs see "Invalid argument" and `datalad fusefs`
prints the actual error, instead of fusepy's "Numerical result out of
range".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TZnKRgtotR2MqJqkzJfvRU
@yarikoptic yarikoptic added the minor Increment the minor version when merged label Oct 7, 2026
yarikoptic pushed a commit that referenced this pull request Oct 7, 2026
…xception"

This reverts commit 93a0bc5.

No longer needed: #139 replaced fusepy with mfusepy, which has the
FUSE._wrapper bugs fixed.  With fusepy gone, the patch module would
even break importing datalad_fuse.fuse_, as it imports fuse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NoaVLBBnhaZqmhrk813G2Z
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor Increment the minor version when merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant