System Info
transformers version: 5.5.3
- Python: 3.13
- Platform: Linux
Who can help?
@Cyrilvallez (model loading)
Information
Tasks
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:
- 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).
- 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)
Workaround
This is what we've used to workaround the problem:
copymode_old = shutil.copymode
try:
shutil.copymode = lambda *_args, **_kwargs: None
model.save_pretrained(path)
finally:
shutil.copymode = copymode_old
System Info
transformersversion: 5.5.3Who can help?
@Cyrilvallez (model loading)
Information
Tasks
examplesfolder (such as GLUE/SQuAD, ...)Reproduction
When a model is registered via
register_for_auto_class,save_pretrainedcopies the user's custom-model.pyfile(s) into the output folder usingshutil.copy. Sinceshutil.copyiscopyfile+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 asr--r--r--untilp4 edit-- the saved copy in the output dir is also read-only.This:
save_pretrainedreturns; in the minimal repro below, the patching step fails withPermissionError).Repro:
Run:
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, incustom_object_save()(lines 646–659):shutil.copypreserves permission bits. The same applies to the three call sites inget_cached_module_file(lines 423, 431, 445).Suggested fix
Replace
shutil.copywithshutil.copyfileat all five call sites indynamic_module_utils.py.copyfilecopies file contents only; the destination, when newly created, gets standard umask-based permissions, which is what callers ofsave_pretrainedreasonably expect.Workaround
This is what we've used to workaround the problem: