transformers - ✅(Solved) Fix save_pretrained` (with `register_for_auto_class`) propagates read-only permissions from custom-model source files [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
huggingface/transformers#45684Fetched 2026-04-29 06:11:23
View on GitHub
Comments
1
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
commented ×1cross-referenced ×1labeled ×1mentioned ×1

Error Message

$ chmod u-w custom_model.py $ python main.py save --path=pretrained_c --magic="Magic C" ... PermissionError: [Errno 13] Permission denied: 'pretrained_c/custom_model.py'

Root Cause

transformers/dynamic_module_utils.py, in custom_object_save() (lines 646–659):

result = []
# Copy module file to the output folder.
object_file = sys.modules[obj.__module__].__file__
dest_file = Path(folder) / (Path(object_file).name)
shutil.copy(object_file, dest_file)
result.append(dest_file)

# Gather all relative imports recursively and make sure they are copied as well.
for needed_file in get_relative_import_files(object_file):
    dest_file = Path(folder) / (Path(needed_file).name)
    shutil.copy(needed_file, dest_file)
    result.append(dest_file)

return result

shutil.copy preserves permission bits. The same applies to the three call sites in get_cached_module_file (lines 423, 431, 445).

Fix Action

Fix / Workaround

This:

  1. Breaks any post-save tooling that wants to rewrite the saved module file (in our real workflow we patch the saved file after save_pretrained returns; in the minimal repro below, the patching step fails with PermissionError).
  2. Leaves users with a saved-model directory full of read-only files that are surprising and awkward to operate on.

PR fix notes

PR #45686: Fix custom-module copies inheriting read-only permissions

Description (problem / solution / changelog)

What does this PR do?

Fixes #45684.

shutil.copy is copyfile + copymode, so when source files are read-only (common with version-control systems like Perforce that check files out as r--r--r-- until edit), the destination inherits those permissions. This breaks post-save tooling that wants to rewrite the saved module file, and leaves users with read-only files in their saved-model directories.

The fix swaps shutil.copy for shutil.copyfile at all five call sites in dynamic_module_utils.py (one in custom_object_save, four in get_cached_module_file). copyfile copies file contents only; the destination, when newly created, gets standard umask-based permissions, which is what callers of save_pretrained expect.

A regression test is added to tests/utils/test_dynamic_module_utils.py: it makes a source module read-only, calls custom_object_save, and asserts the destination is writable. The test fails on main and passes with this fix.

Code Agent Policy

  • I confirm that this is not a pure code agent PR.
    • AI was used for making the change, but I hand-edited, reviewed and added tests myself.

Before submitting

Who can review?

@Cyrilvallez (model loading)

Changed files

  • src/transformers/dynamic_module_utils.py (modified, +5/-5)
  • tests/utils/test_dynamic_module_utils.py (modified, +31/-1)

Code Example

$ chmod u-w custom_model.py
$ python main.py save --path=pretrained_c --magic="Magic C"
...
PermissionError: [Errno 13] Permission denied: 'pretrained_c/custom_model.py'

---

result = []
# Copy module file to the output folder.
object_file = sys.modules[obj.__module__].__file__
dest_file = Path(folder) / (Path(object_file).name)
shutil.copy(object_file, dest_file)
result.append(dest_file)

# Gather all relative imports recursively and make sure they are copied as well.
for needed_file in get_relative_import_files(object_file):
    dest_file = Path(folder) / (Path(needed_file).name)
    shutil.copy(needed_file, dest_file)
    result.append(dest_file)

return result

---

-    shutil.copy(object_file, dest_file)
+    shutil.copyfile(object_file, dest_file)
RAW_BUFFERClick to expand / collapse

System Info

  • transformers version: 5.5.3
  • Python: 3.13
  • Platform: Linux

Who can help?

@Cyrilvallez (model loading)

Information

  • The official example scripts
  • My own modified scripts

Tasks

  • An officially supported task in the examples folder (such as GLUE/SQuAD, ...)
  • My own task or dataset (give details below)

Reproduction

When a model is registered via register_for_auto_class, save_pretrained copies the user's custom-model .py file(s) into the output folder using shutil.copy. Since shutil.copy is copyfile + copymode, the destination inherits the source file's permission bits. If the source is read-only -- a common state for files managed by Perforce, which checks files out as r--r--r-- until p4 edit -- the saved copy in the output dir is also read-only.

This:

  1. Breaks any post-save tooling that wants to rewrite the saved module file (in our real workflow we patch the saved file after save_pretrained returns; in the minimal repro below, the patching step fails with PermissionError).
  2. Leaves users with a saved-model directory full of read-only files that are surprising and awkward to operate on.

Repro:

Run:

$ chmod u-w custom_model.py
$ python main.py save --path=pretrained_c --magic="Magic C"
...
PermissionError: [Errno 13] Permission denied: 'pretrained_c/custom_model.py'

main.py custom_model.py

Expected behavior

Files written into the save directory should be writable by the user (subject to the standard umask), independent of the source file's mode bits.

Root cause

transformers/dynamic_module_utils.py, in custom_object_save() (lines 646–659):

result = []
# Copy module file to the output folder.
object_file = sys.modules[obj.__module__].__file__
dest_file = Path(folder) / (Path(object_file).name)
shutil.copy(object_file, dest_file)
result.append(dest_file)

# Gather all relative imports recursively and make sure they are copied as well.
for needed_file in get_relative_import_files(object_file):
    dest_file = Path(folder) / (Path(needed_file).name)
    shutil.copy(needed_file, dest_file)
    result.append(dest_file)

return result

shutil.copy preserves permission bits. The same applies to the three call sites in get_cached_module_file (lines 423, 431, 445).

Suggested fix

Replace shutil.copy with shutil.copyfile at all five call sites in dynamic_module_utils.py. copyfile copies file contents only; the destination, when newly created, gets standard umask-based permissions, which is what callers of save_pretrained reasonably expect.

-    shutil.copy(object_file, dest_file)
+    shutil.copyfile(object_file, dest_file)

Happy to send a PR if this looks right.

extent analysis

TL;DR

Replace shutil.copy with shutil.copyfile in dynamic_module_utils.py to ensure saved files have writable permissions.

Guidance

  • Identify the call sites of shutil.copy in dynamic_module_utils.py and replace them with shutil.copyfile to change the permission behavior.
  • Verify the fix by checking the permissions of the saved files after running save_pretrained.
  • Test the post-save tooling to ensure it can rewrite the saved module files without encountering PermissionError.
  • Consider sending a PR with the suggested fix to update the transformers library.

Example

# Before
shutil.copy(object_file, dest_file)

# After
shutil.copyfile(object_file, dest_file)

Notes

This fix assumes that the standard umask-based permissions are desired for the saved files. If custom permissions are required, additional modifications may be needed.

Recommendation

Apply the workaround by replacing shutil.copy with shutil.copyfile in dynamic_module_utils.py, as this change should resolve the permission issue without introducing significant side effects.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Files written into the save directory should be writable by the user (subject to the standard umask), independent of the source file's mode bits.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

transformers - ✅(Solved) Fix save_pretrained` (with `register_for_auto_class`) propagates read-only permissions from custom-model source files [1 pull requests, 1 comments, 1 participants]