All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
No unreleased changes.
0.4.0 — 2026-06-05
- Root symbolic links are rejected when symbolic link traversal is disabled (
follow_symlinks=False), including directory and file symlinks used as the scan root. - SQLite cache database and all auxiliary files (
-wal,-shm,-journal) are excluded from scans. - Cache exclusion works correctly when the scan root is accessed through a symbolic link, using
resolve(strict=False)on both sides of the comparison. - Internal path comparison during cache exclusion no longer raises
OSErrororRuntimeErroron symlink loops or broken symlinks. - PyPI publishing workflow (
publish.yml) rewritten as valid YAML — the previous file contained Markdown code fences that made it unparseable by GitHub Actions. - Public type annotations in
ScanOptions,ScanEvent, andScanProgressnow resolve correctly on all supported Python versions viaget_type_hints().
- Cancelled CLI scans return exit status
3. Exit code3takes priority over--fail-on-duplicates. - Minimum supported Python version raised from 3.9 to 3.10. The codebase uses
X | Yunion syntax broadly; requiring 3.10 makes this consistent and allowsget_type_hints()to resolve annotations correctly at runtime. ProgressPhasevalues restricted to the phases actually emitted:"discovery","hashing", and"done".
ScanEvent.from_cacheandScanEvent.bytes_readfields removed. These fields were added in 0.3.0 but were never populated — they always remained at their default values (Falseand0). Removing them eliminates misleading API surface.
- CI restructured into three jobs:
quality(Ruff lint + format check + Pyright on Ubuntu),tests(matrix across Ubuntu, Windows, and macOS with Python 3.10–3.13), andpackage(build, Twine check,py.typedverification, wheel smoke test). - Publishing workflow (
publish.yml) now runs Ruff, Pyright, and full quality checks before building, so manual releases cannot skip linting. pyyamladded to development dependencies to enable workflow YAML validation in tests.- Regression tests added: symlink policy, cache exclusion (including symlinked roots, symlink loops, broken symlinks), typing contracts, cancellation exit codes, version consistency,
py.typedmarker, workflow YAML validation.
0.3.0 — 2026-06-04
DupeFinderclass — integration-ready scan engine with typed events, cancellation, and optional hash cache.ScanEventfrozen dataclass — typed events emitted during each scan phase (scan_started,file_discovered,file_hashed,duplicate_group_found,issue,scan_completed,scan_cancelled).ScanEvent.from_cacheandScanEvent.bytes_readfields (reserved; removed in 0.4.0).ScanProgressfrozen dataclass — simplified progress snapshots delivered to the newon_progresscallback. Fields:root,phase,scanned_files,hashed_files,total_candidates,duplicate_groups,elapsed_seconds,cancelled.DupeFinder.on_progressparameter — optional callback that receives aScanProgresssnapshot after each file discovered or hashed, and once more at the end withphase="done".ScanOptions.max_files— stop file discovery after N files.ScanOptions.max_depth— limit directory recursion depth (0= root only).ScanOptions.timeout_seconds— automatically cancel scan after N seconds.ScanReport.cancelled—Truewhen the scan was cancelled early.ScanReport.elapsed_seconds— wall-clock scan duration.ScanReport.total_bytes_read— total bytes read during hashing.ScanReport.to_dict()andScanReport.to_json()— convenience serialization methods.DuplicateGroup.to_dict()andScanIssue.to_dict()— serialize to plain dictionary.HashCacheprotocol — interface for pluggable hash caches.SQLiteHashCache— SQLite-backed persistent hash cache; entries validated by file size and mtime.grouping.candidate_files()andgrouping.groups_from_hash_map()— public helpers._ScanCancelledinternal exception inerrors.py.hash_file()andhash_files()acceptshould_cancelandon_bytes_readcallbacks.SCHEMA_VERSION = "1.1"constant;schema_versionandtotal_bytes_readin all JSON/dict output.- CLI flags:
--max-files,--max-depth,--timeout,--cache,--progress. --progressflag prints live discovery/hashing progress to stderr.
- Engine now emits
type="issue"events for all issues collected during discovery and hashing. - Cache error handling catches
sqlite3.Errorin addition toOSError. - CLI cache creation moved inside the
tryblock so errors on cache open are caught and reported cleanly with exit code 1. - Engine no longer duplicates size-grouping and hash-grouping logic from
grouping.py.
SCHEMA_VERSIONbumped from"1.0"to"1.1".dupefinder.__version__updated to"0.3.0".- Development status classifier updated from
3 - Alphato4 - Beta.
0.1.0 — 2026-06-04
scan(path, options)— returns a completeScanReportwith duplicate groups, file counts, and any issues encountered.find_duplicates(path, options)— simplified function that returns only the duplicate groups.ScanOptions— frozen dataclass to configure every aspect of a scan (algorithm, chunk size, size filters, hidden-file handling, symlink following, error handling).ScanReport,DuplicateGroup,FileInfo,ScanIssue— typed, immutable data models for all scan results.- Chunked file hashing via
hashlib— any algorithm available inhashlib.algorithms_availableis supported. - Two-pass duplicate detection: group by file size first, hash only candidates.
format_report()— human-readable text output.report_to_json()/report_to_dict()— structured JSON/dict output.dupefinderCLI command with flags:--algorithm,--chunk-size,--min-size,--max-size,--include-ext,--ignore-ext,--no-ignore-hidden,--follow-symlinks,--strict,--json,--fail-on-duplicates,--version.- Custom exception hierarchy:
DupeFinderError,InvalidPathError,UnsupportedHashAlgorithmError,InvalidOptionError,FileAccessError,FileHashError. - Symlink loop detection in directory traversal.
- Unit tests for all modules using only the Python standard library.
- Zero runtime dependencies — standard library only.