Perform atomic edit of files through a temporary copy
Project description
temper-edit
Overview
temper-edit is a command-line tool that performs atomic file editing through a temporary copy. It provides a safe way to edit files by ensuring the original is only modified when the editing has finished successfully.
How It Works
- Creates a temporary copy of the target file
- Launches your configured editor on the temporary copy
- If the editor exits successfully and content changed, atomically replaces the original
- Preserves the original file's ownership and permissions during replacement
- If content is unchanged, the original file remains untouched
Highlights
- Privilege escalation protection: Refuses to run when invoked via
sudo,doas, orpkexec— use--elevateinstead for editing privileged files - Atomic updates: Other processes watching the file see either the old or new content, never a partial write
- Secure replacement: Uses a permission-controlled sequence to prevent race conditions and privilege attacks
- Failure recovery: Preserves the temporary file on editor failure, allowing manual recovery
- Full test coverage: Comprehensive test suite with 100% code coverage, tested across multiple Python versions in isolated containers
Installation
Requirements
- Python 3.10 or later
- No runtime dependencies (S3 support requires optional
boto3dependency)
From PyPI
pip install temper-edit
With S3 Support
pip install "temper-edit[s3]"
Usage
Basic Usage
temper-edit <filename>
Edit a file using your configured editor. The original file is only updated if the editor exits successfully and the content has changed.
Options
| Option | Description |
|---|---|
filename |
Path to the file to edit |
--tmpdir <path> |
Directory for temporary files (optional) |
--elevate <program> |
Privilege escalation program to use for editing root-owned files (e.g., sudo, doas) |
--s3 <bucket> |
Treat filename as an S3 key in the specified bucket |
--force |
Force overwrite even if the S3 object was modified concurrently |
Editor Selection
temper-edit selects the editor based on environment variables, checked in this order:
SUDO_EDITOR(highest priority)VISUALEDITOR
If none are set, temper-edit exits with an error.
The editor value is parsed using shell quoting rules, so complex commands work:
export EDITOR="vim -u NONE"
temper-edit myfile.txt
Temporary Directory
By default, temporary files are created in the system's default temporary directory. Override this with:
# Via command-line argument (highest priority)
temper-edit --tmpdir /my/tmpdir myfile.txt
# Via environment variable
TMPDIR=/my/tmpdir temper-edit myfile.txt
Examples
Edit a configuration file:
temper-edit /etc/myapp/config.yaml
Edit with a specific temporary directory:
temper-edit --tmpdir /secure/tmp /etc/myapp/config.yaml
Edit a root-owned file (requires sudo/doas privileges):
temper-edit --elevate sudo /etc/hosts
temper-edit --elevate doas /etc/hosts
or other alternative programs.
The --elevate option supports multi-component commands with additional flags:
temper-edit --elevate "sudo --askpass" /etc/hosts
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Unexpected error |
| 10 | Configuration error (no editor configured, bad tmpdir) |
| 11 | Privilege escalation detected (running under sudo/doas/pkexec) |
| 12 | Missing optional dependency (e.g., boto3 for S3 support) |
| 20 | Staging failed |
| 21 | File not found during staging |
| 22 | Permission denied during staging |
| 30 | Editor failed (not found or non-zero exit) |
| 40 | Commit failed |
| 41 | Permission denied during commit |
| 42 | Concurrent modification detected (S3 only) |
S3 Editing
Edit an S3 object:
temper-edit --s3 my-bucket path/to/object.txt
AWS credentials are read from the standard environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or AWS configuration files.
Metadata Preservation
When editing S3 objects, the following attributes are preserved after a successful update:
| Attribute | Preserved |
|---|---|
Custom metadata (x-amz-meta-*) |
Yes |
| Content-Type | Yes |
| Content-Encoding | No |
| Content-Disposition | No |
| Cache-Control | No |
| Expires | No |
Attributes not listed above (such as ETag, Last-Modified, and storage class) are managed by S3 and may change as part of the update.
Concurrent Modification Detection
temper-edit uses S3 ETags to detect concurrent modifications. If the object is modified by another process after staging but before commit, the upload fails with an error:
Failed to update file: path/to/object.txt (concurrent modification detected). Temporary file preserved at: /tmp/tmpXXXXXX
This prevents silent data loss when multiple processes edit the same object. The temporary file containing your edits is preserved for manual recovery.
To force an overwrite regardless of concurrent modifications:
temper-edit --s3 my-bucket --force path/to/object.txt
Recovery workflow when concurrent modification is detected:
- The error message shows the path to your preserved edits
- Re-download the current object to see what changed:
aws s3 cp s3://bucket/key current.txt - Manually merge your changes from the temp file
- Either re-run temper-edit or use
--forceif you're certain your version should win
Testing Philosophy
Full Coverage
The test suite maintains 100% coverage of both lines and branches. This is enforced in CI and ensures that every code path is exercised. Full coverage is achievable and maintainable. Validation gaps tend to grow over time, so maintaining complete coverage prevents technical debt accumulation.
Behavior-Driven Tests
Tests demonstrate actual behavior rather than implementation details. This includes verifying safety properties under anomalous conditions. For example:
- Atomicity: The original file is never left in a partial state, even when the editor crashes mid-edit
- Permission preservation: File ownership and mode bits survive the replacement sequence
- Failure recovery: Temporary files are preserved when operations fail, allowing manual recovery
- Security: The commit sequence prevents race conditions and privilege escalation attacks
Mock-Free Testing
The test suite uses no mocks. Instead, it relies on:
- Testability by design: Code architecture uses dependency injection and factory patterns, making components naturally testable without stubbing internals
- Containerized execution: Tests run inside isolated containers across multiple Python versions, exercising real system calls, file operations, and privilege boundaries
- Real services: For example, S3 tests use MinIO containers rather than mocked AWS clients, ensuring the code works against actual S3-compatible APIs
This approach catches integration issues that mocks would hide and provides confidence that the code behaves correctly in production environments.
Licensing
This project is distributed under the terms of GPL-3.0-or-later. In addition, it uses
- CC-BY-SA-4.0 for documentation files
- CC0-1.0 (equivalent to public domain) for project configuration files
The REUSE tool is used to ensure license compliance.
© 2025-2026 Ohad Livne
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file temper_edit-1.4.1.tar.gz.
File metadata
- Download URL: temper_edit-1.4.1.tar.gz
- Upload date:
- Size: 53.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":null,"id":"forky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fe654c3dc07d06bf535f09f4df868ded781f8f6ae274b715d0276f055843d8c
|
|
| MD5 |
c53b0cc91f7efb647f89e79dfe2307a3
|
|
| BLAKE2b-256 |
ff5fa539dea70939db77f5fc4608537b21c0d4128186db0334f0e9358df138ed
|
File details
Details for the file temper_edit-1.4.1-py3-none-any.whl.
File metadata
- Download URL: temper_edit-1.4.1-py3-none-any.whl
- Upload date:
- Size: 45.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":null,"id":"forky","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62084909549462c26fe8126f9342f73ad4a1d8b90badb9621100d656df0c57a1
|
|
| MD5 |
a1b2462657e957ce06774b5e543aadc9
|
|
| BLAKE2b-256 |
f0ec802af16d0dd9e79e8e2023187644e6f6f9961143428ca8b7a6fb48cda716
|