To create a new Python report without replacing an existing file, open the destination with mode 'x', not 'w'. Exclusive creation fails if the path already exists. This is a useful default for exports where a second run should stop instead of silently erasing yesterday’s result.
The important boundary is that exclusive creation protects the initial creation step. It does not guarantee a complete export, reserve disk capacity, validate the report contents or prevent later modification by another process.
Why checking exists before writing is weaker
A tempting pattern is to check whether a path exists and then open it for writing. These are separate operations. Another program could create the destination after your check but before your write. Opening with 'w' can then truncate that newly created file.
Python’s official open documentation describes 'w' as writing with truncation and 'x' as exclusive creation that fails when the file already exists. Express the refusal in the open operation rather than making a preliminary check your only protection. This is not a claim that an arbitrary shared filesystem is a secure sandbox; deployment-specific storage behaviour and hostile path changes need a separate review.
Try a disposable example with an existing-file test
This Python 3 example writes only inside an automatically cleaned temporary directory. Save it as a practice script and run it with your Python 3 interpreter. It creates one report, attempts the same destination again and verifies that the first result survived.
from pathlib import Path
from tempfile import TemporaryDirectory
def create_report(destination, text):
with open(destination, 'x', encoding='utf-8', newline='') as stream:
stream.write(text)
with TemporaryDirectory() as workspace:
report = Path(workspace) / 'inventory.txt'
create_report(report, 'approved inventory')
original = report.read_bytes()
try:
create_report(report, 'replacement inventory')
except FileExistsError:
print('Stopped: destination already exists')
else:
raise AssertionError('Expected an existing-file refusal')
assert report.read_bytes() == original
print('Original content preserved')
missing_parent = Path(workspace) / 'missing' / 'report.txt'
try:
create_report(missing_parent, 'inventory')
except FileNotFoundError:
print('Stopped: parent directory is missing')
else:
raise AssertionError('Expected a missing-parent error')
The first call succeeds because inventory.txt does not yet exist. The second must raise FileExistsError, which is the specific collision condition documented by Python. The byte comparison checks preservation, not merely that an exception occurred. The final case demonstrates a different failure: exclusive creation does not create missing parent directories.
Handle a collision without hiding other failures
In a real command-line tool, report the destination clearly and exit unsuccessfully when FileExistsError occurs. Do not print a success message afterwards. Ask the operator to inspect the existing report and deliberately choose a new output path, or implement a documented naming policy that still uses exclusive creation on every candidate.
A timestamp is a naming convention, not a collision guarantee. Two runs can choose the same name, so keep the creation guard even with dated exports. Appending with 'a' is also not a substitute: it deliberately adds content to an existing file and may combine incompatible report versions.
Do not catch every OSError and label it ‘already exists’. Permission problems, a missing parent and storage failures are different conditions. Preserve the actual error for diagnosis, avoiding sensitive path details in logs that other users can read.
No overwrite does not mean an all-or-nothing report
Once the new file is opened, a write can fail partway through. The destination may then exist but contain incomplete data. A retry should not automatically delete that file or switch to overwrite mode. Inspect it, preserve useful evidence and apply a deliberate recovery procedure.
Build and validate a small report’s text before opening its output file where practical. For larger exports, design a separate completion protocol appropriate to the reader and filesystem. Do not advertise this short example as a crash-safe transaction, a lock covering the whole workflow or a backup system.
Input safety is another separate task. If the export reads files selected by a user, see checking whether a Python input file stays inside an approved folder. Output collision refusal does not validate input paths.
Checklist before adapting the function
- Confirm that refusing a repeat output is the intended behaviour.
- Choose the parent folder explicitly and verify it exists.
- Use an explicit text encoding; use binary mode for bytes instead.
- Test a fresh path, an existing path and a missing parent.
- Keep error handling distinct from successful report completion.
- Plan how incomplete outputs are identified and reviewed.
Can I use a Path object? Yes. Python’s open accepts path-like objects, as used above. Will this change an old report? The existing-file attempt is refused, but this is not protection against unrelated programs that can modify that report.
Source: Python 3.12 open documentation, modes and FileExistsError behaviour.
Educational purposes only. Test carefully before production use. The site owner is not responsible for misuse, damage, data loss or production issues.



