Python’s shutil.copytree copies a directory tree, but it has no documented preview or dry-run mode. To see what a copy will touch before any file changes, you build the plan yourself: list the paths that will be created, skipped, and possibly overwritten, show that list, and only then call copytree with settings you chose on purpose.
Why copytree alone is not a preview
When you call copytree, it performs the copy immediately. It does not return a list of what it would do. A preview-first organizer therefore has two separate stages: a planning stage that walks the source tree and records proposed operations, and an execution stage that calls copytree using the same settings the user approved. The Python Software Foundation’s shutil — High-level file operations reference, checked on 7 October 2026, documents the function’s behavior and options; it does not describe a preview feature.
Treat the preview as a plan, not a guarantee. Source and destination contents can change between review and execution, so the execution stage should re-check the destination state and report what actually happened rather than repeat the plan as if it were fact.
The settings that change what gets written
Four documented options determine most of the outcome. Each one should appear in the preview, because each one can change which files are written or replaced.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →#1 Best Overall
- Easily store and access 2TB to content on the go with the Seagate Portable Drive, a USB external hard drive
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
| Setting | Default | Effect | What the preview should show |
|---|---|---|---|
dirs_exist_ok |
False |
If the destination exists, the default raises FileExistsError. With True, copying continues into existing directories and matching destination files can be overwritten. |
Whether the destination root exists, and every destination file that already exists at a planned path. |
symlinks |
False |
With False, the contents and metadata of the linked-to file are copied. With True, links are reproduced as links as far as the platform allows. |
Each link found in the source, the chosen treatment, and any dangling link. |
ignore or ignore_patterns |
None | Names returned by the callback, or matching the glob patterns, are skipped during the copy. | Every excluded path, so the user can confirm nothing important is silently left out. |
copy_function |
copy2 |
Copies each file. copy2 attempts to preserve metadata. |
A note that metadata preservation is best-effort and depends on the platform. |
The first four rows come from the documented API. The “what the preview should show” column is a design recommendation for a preview-first interface, not behavior built into shutil.
Step by step: the preview-first workflow
- Validate the inputs. Confirm the source exists and is a directory. Resolve both paths to absolute paths so the plan shows exactly what will be used.
- Build the plan. Walk the source tree, apply the same exclusion rule that the copy will use, and record each planned file, each excluded name, and each destination file that already exists.
- Show the plan. Print the source, the destination, the counts, the exclusions, and the overwrite list. Do not hide the overwrite list behind a summary count.
- Make the destination choice explicit. If the destination exists, ask the user to choose between stopping and merging. Only merge when the user has selected that option for this run.
- Execute with the approved settings. Pass the same
ignore,dirs_exist_ok, andsymlinksvalues that the preview displayed. - Report the outcome. Surface every failure from
shutil.Errorand do not describe the copy as complete if any item failed.
A planning function that mirrors the copy
The following sketch uses os.walk and the same shutil.ignore_patterns helper that the copy will use. It records planned files, exclusions, and overwrite candidates without writing anything. It does not follow directory symlinks, so if your symlink policy includes directory links, extend the walk to match before relying on the preview.
Rank #2
- Easily store and access 1TB to content on the go with the Seagate Portable Drive, a USB external hard drive.Specific uses: Personal
- Designed to work with Windows or Mac computers, this external hard drive makes backup a snap just drag and drop. Reformatting may be required for Mac
- To get set up, connect the portable hard drive to a computer for automatic recognition no software required
- This USB drive provides plug and play simplicity with the included 18 inch USB 3.0 cable
- The available storage capacity may vary.
import os
import shutil
from pathlib import Path
def build_plan(src, dst, patterns=()):
src = Path(src).resolve()
dst = Path(dst).resolve()
skip_fn = shutil.ignore_patterns(*patterns) if patterns else (lambda d, names: set())
plan = {"copy": [], "ignored": [], "overwrites": [], "dst_exists": dst.exists()}
for dirpath, dirnames, filenames in os.walk(src):
current = Path(dirpath)
names = dirnames + filenames
skipped = skip_fn(dirpath, names)
for name in names:
if name in skipped:
plan["ignored"].append((current / name).relative_to(src))
dirnames[:] = [d for d in dirnames if d not in skipped]
for name in filenames:
if name in skipped:
continue
rel = (current / name).relative_to(src)
plan["copy"].append(rel)
if (dst / rel).exists():
plan["overwrites"].append(rel)
return plan
Print plan["copy"], plan["ignored"], and plan["overwrites"] before asking for confirmation. If plan["dst_exists"] is true, the user must choose a destination policy before the run continues.
Destination policy: stop or merge
The default behavior is safe in one specific sense: if the destination exists and dirs_exist_ok is left at False, the standard library refuses to proceed. The reference states: “If dirs_exist_ok is false (the default) and dst already exists, a FileExistsError is raised.” Source: Python Software Foundation, shutil — High-level file operations, https://docs.python.org/3/library/shutil.html?highlight=shutil.rmtree.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchRank #3
- High capacity in a small enclosure – The small, lightweight design offers up to 6TB* capacity, making WD Elements portable hard drives the ideal companion for consumers on the go.
- Plug-and-play expandability
- Vast capacities up to 6TB[1] to store your photos, videos, music, important documents and more
- SuperSpeed USB 3.2 Gen 1 (5Gbps)
Setting dirs_exist_ok=True lets the copy continue into existing directories, and matching destination files can be replaced. Make this a visible choice in the interface, not a silent default. In the preview, list each file from the overwrite set so the user can see which existing files will be replaced.
Symlinks: keep links or copy targets
The symlinks option is a policy decision, not a technical detail. With the default symlinks=False, the linked-to contents and metadata are copied into the destination. With symlinks=True, links are reproduced as links where the platform allows it. A dangling link, one whose target does not exist, can produce an error in the default mode; the copy collects it and reports it at the end.
Rank #4
- Plug-and-play expandability
- SuperSpeed USB 3.2 Gen 1 (5Gbps)
When your source tree contains links, the preview should list each link and state which treatment the run will apply. If you copy targets, a link to a large directory elsewhere on the disk can silently multiply the amount of data written, so show the user the target path as well as the link path.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Exclusions: glob patterns or a callback
Use shutil.ignore_patterns("*.tmp", "__pycache__") for simple name-based exclusions. Use a custom ignore callback when the rule depends on context, such as skipping a folder only at the top level. The callback is called recursively and returns the names to skip for each directory, so the preview and the copy should share one function to keep their results identical.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Best Value
- 【Upgraded version】 - The mirror logo strip is combined with the striped non-slip design. The rounded corners of the shell are more suitable for holding. The strips play a heat dissipation function to ensure a stable and fast transmission process.
- 【Ultra-thin and quiet】 - The motherboard adopts JMicron 578 noise-free solution, giving you a quiet working environment. Lightweight and portable size designed to fit in your pocket for easy portability.
- 【Ultra-Fast Data Transfers】 - Pairing this external hard drive with JMicron 578 solution USB 3.0 and USB 2.0 interfaces enables blazing-fast data transfer. It boasts theoretical read speeds of up to 125MB/s and write speeds of up to 103MB/s.
- 【Plug and Play】 - With no software to install, just plug it in and the drive is ready to use.The hard disk chip is wrapped with an aluminum anti-interference layer to increase heat dissipation and protect data.
- 【What You Get】 - 1 x Portable Hard Drive, 1 x USB 3.0 Cable, 1 x User Manual, Gift-type shell packaging ,Three-year manufacturer's warranty and free technical support services.
Show every excluded path in the preview. Silent exclusion is the most common reason a copy looks complete while missing files the user expected.
Reporting failures honestly
Failures during a copytree call are collected and raised together as shutil.Error. Catch that exception, print each failed item, and return a non-success status. The example below also handles the case where the destination exists and the policy was to stop.
import shutil
def run_copy(src, dst, patterns=(), dirs_exist_ok=False, symlinks=False):
ignore = shutil.ignore_patterns(*patterns) if patterns else None
try:
shutil.copytree(src, dst, ignore=ignore,
dirs_exist_ok=dirs_exist_ok, symlinks=symlinks)
except FileExistsError:
print("Destination exists and dirs_exist_ok is False. Nothing was copied.")
return False
except shutil.Error as exc:
for src_item, dst_item, reason in exc.args[0]:
print(f"FAILED {src_item} -> {dst_item}: {reason}")
return False
return True
Pass the same arguments you showed in the preview. If the preview displayed one set of settings and the run used another, the preview has no value.
What a copy does not preserve
A high-level copy cannot preserve all metadata on all platforms. The standard library reference documents these platform limits, and the table below summarizes them. Do not describe this workflow as an archival or forensic copy.
| Platform | Not retained by a shutil copy, per the reference |
|---|---|
| POSIX | Owner, group, and ACL information. |
| macOS | Resource forks and some other metadata. |
| Windows | Owner, ACL, and alternate data stream information. |
The exact outcome can depend on the platform and the filesystem. Beginning with Python 3.8, copy functions may use platform-specific fast-copy system calls. That affects speed, not the overwrite and metadata behavior your preview must disclose.
Quick Recap
Checklist before you run the copy
- The source is a directory and both paths are absolute.
- The plan lists every planned file, every exclusion, and every existing destination file.
- The destination policy was chosen explicitly, not left to a default you did not inspect.
- The symlink treatment matches what the preview displayed.
- Failures are printed and the run is not reported as complete when any item failed.
- You have tested the plan on the operating systems and filesystems you use, since metadata results vary.
”
The Bottom Line
“”
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




