Skip to content

Commit 2f8b035

Browse files
committed
fix(cli): prevent temp fd leak and align merge-policy docs
1 parent d87ae7a commit 2f8b035

File tree

1 file changed

+16
-12
lines changed

1 file changed

+16
-12
lines changed

src/specify_cli/__init__.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -668,13 +668,17 @@ def log(message, color="green"):
668668

669669
def atomic_write_json(target_file: Path, payload: dict[str, Any]) -> None:
670670
"""Atomically write JSON while preserving existing mode bits when possible."""
671-
fd, temp_path = tempfile.mkstemp(
672-
dir=target_file.parent,
673-
prefix=f"{target_file.name}.",
674-
suffix=".tmp",
675-
)
671+
temp_path: Optional[Path] = None
676672
try:
677-
with os.fdopen(fd, 'w', encoding='utf-8') as f:
673+
with tempfile.NamedTemporaryFile(
674+
mode='w',
675+
encoding='utf-8',
676+
dir=target_file.parent,
677+
prefix=f"{target_file.name}.",
678+
suffix=".tmp",
679+
delete=False,
680+
) as f:
681+
temp_path = Path(f.name)
678682
json.dump(payload, f, indent=4)
679683
f.write('\n')
680684

@@ -694,8 +698,8 @@ def atomic_write_json(target_file: Path, payload: dict[str, Any]) -> None:
694698

695699
os.replace(temp_path, target_file)
696700
except Exception:
697-
if os.path.exists(temp_path):
698-
os.unlink(temp_path)
701+
if temp_path and temp_path.exists():
702+
temp_path.unlink()
699703
raise
700704

701705
try:
@@ -726,8 +730,8 @@ def merge_json_files(existing_path: Path, new_content: Any, verbose: bool = Fals
726730
727731
Performs a polite deep merge where:
728732
- New keys are added
729-
- Existing keys are PRESERVED (not overwritten) unless they are dictionaries
730-
- Nested dictionaries are merged recursively
733+
- Existing keys are preserved (not overwritten) unless both values are dictionaries
734+
- Nested dictionaries are merged recursively only when both sides are dictionaries
731735
- Lists and other values are preserved from base if they exist
732736
733737
Args:
@@ -783,8 +787,8 @@ def deep_merge_polite(base: dict[str, Any], update: dict[str, Any]) -> dict[str,
783787
# Recursively merge nested dictionaries
784788
result[key] = deep_merge_polite(result[key], value)
785789
else:
786-
# Key already exists and is not a dict, PRESERVE existing value
787-
# This ensures user settings aren't overwritten by template defaults
790+
# Key already exists and values are not both dicts; preserve existing value.
791+
# This ensures user settings aren't overwritten by template defaults.
788792
pass
789793
return result
790794

0 commit comments

Comments
 (0)