A fast, intelligent file copier/mover with hashing, filters, conflict resolution, and backup support — built for power users.
Project description
Fylex: Your Intelligent File & Directory Orchestrator
Fylex is a powerful and flexible Python utility designed to simplify complex file management tasks. From intelligent copying and moving to flattening chaotic directory structures and resolving file conflicts, Fylex provides a robust, concurrent, and log-detailed solution for organizing your digital life.
Table of Contents
- Introduction
- Key Features
- Installation
- Usage
- Why Fylex is Superior
- Error Handling
- Logging
- Development & Contributing
- License
1. Introduction
Managing files can quickly become a tedious and error-prone process, especially when dealing with large collections, duplicate files, or disorganized directory structures. Traditional command-line tools offer basic copy/move functionalities, but often lack the intelligence to handle conflicts, filter effectively, or automate complex reorganization patterns.
Fylex steps in to fill this gap. It's built on a foundation of robust error handling, concurrent processing, and intelligent decision-making, ensuring your file operations are efficient, safe, and tailored to your needs.
2. Key Features
- Smart Copy (
copy_files): Copy files with advanced filtering, conflict resolution, and integrity verification. - Smart Move (
move_files): Move files, similar to copying, but with source file deletion upon successful transfer and verification. - File Hashing for Reliability: Utilizes
xxhashfor fast, non-cryptographic hashing to ensure file integrity post-transfer and detect true content duplicates. - Sophisticated Conflict Resolution: Offers a comprehensive set of strategies to handle name collisions at the destination (e.g., rename, replace, keep larger/smaller/newer/older, skip, or prompt).
- Accident Prevention with Deprecated Folders: Crucially, when an
on_conflictmode leads to an existing destination file being replaced (e.g., by a "newer" or "larger" incoming file), Fylex automatically moves the superseded file into a timestamped.fylex_deprecated/subfolder within the destination directory. This acts as a robust safety net against accidental data loss, allowing you to recover older versions if needed. Additionally, if a source file is skipped because its identical counterpart already exists at the destination, it is also moved tofylex.deprecated/by default. - Flexible File Filtering:
- Inclusion: Specify files to process using regular expressions (
match_regex), exact names (match_names), or glob patterns (match_glob). - Exclusion: Prevent specific files from being processed using
exclude_regex,exclude_names, orexclude_glob. - Junk File Awareness: Predefined
JUNK_EXTENSIONShelps easily exclude common temporary, system, and development artifacts.
- Inclusion: Specify files to process using regular expressions (
- Directory Reorganization Utilities:
spill: Consolidate files from nested subdirectories up to a specified depth into a parent directory.flatten: Move all files from an entire directory tree into a single target directory, then automatically delete the empty subdirectories.
- Concurrency for Speed: Leverages Python's
ThreadPoolExecutorto perform file operations in parallel, significantly speeding up tasks involving many files. - Dry Run Mode: Simulate any operation without making actual changes to the filesystem. Essential for verifying complex commands before execution.
- Interactive Mode: Prompts for user confirmation before each file operation, providing fine-grained control.
- Comprehensive Logging: All actions, warnings, and errors are meticulously logged to
fylex.logfor easy auditing and debugging. - Robust Path Validation: Prevents common pitfalls like attempting to copy a directory into itself, or operating on non-existent paths.
- Retry Mechanism: Failed file operations are retried up to
MAX_RETRIESto handle transient network issues or temporary file locks.
3. Installation
Fylex is designed to be integrated into your Python projects or run as a standalone script.
- Clone the repository:
git clone [https://github.com/your-repo/fylex.git](https://github.com/your-repo/fylex.git) # Replace with your actual repo cd fylex
- Install dependencies:
Fylex requires
xxhash.pip install xxhash
- Include in your project:
You can import Fylex functions directly into your Python scripts:
from fylex import copy_files, move_files, spill, flatten, delete_empty_dirs from fylex.exceptions import InvalidPathError, PermissionDeniedError
4. Usage
Fylex functions are designed to be intuitive. Here's a breakdown of the core functions and their parameters.
Core Functions Overview
copy_files(src, dest, **kwargs): Copies files fromsrctodest.move_files(src, dest, **kwargs): Moves files fromsrctodest.spill(target, **kwargs): Moves files from subdirectories withintargettotarget.flatten(target, **kwargs): Moves all files from subdirectories withintargettotargetand deletes empty subdirectories.delete_empty_dirs(target): Recursively deletes all empty subdirectories withintarget.
Common Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
src |
str |
N/A |
Source path (file or directory) for copy_files and move_files. |
dest |
str |
N/A |
Destination path (directory) for copy_files and move_files. |
target |
str |
N/A |
Target directory path for spill, flatten, and delete_empty_dirs. |
no_create |
bool |
False |
If True, prevents Fylex from creating the dest directory if it doesn't exist. An InvalidPathError will be raised. |
interactive |
bool |
False |
If True, Fylex will prompt the user for confirmation before each file operation. |
dry_run |
bool |
False |
If True, Fylex will simulate all operations without making any changes to the filesystem. Useful for testing and verification. Logs will indicate what would have happened. |
match_regex |
str |
None |
A regular expression string to include files whose names fully match the pattern. Overrides match_glob. |
match_names |
list[str] |
None |
A list of exact file names to include. Case-sensitive. |
match_glob |
str |
None |
A Unix-style glob pattern to include files whose names match. Converted to regex internally. |
exclude_regex |
str |
None |
A regular expression string to exclude files whose names fully match the pattern. Overrides exclude_glob. |
exclude_names |
list[str] |
None |
A list of exact file names to exclude. Case-sensitive. |
exclude_glob |
str |
None |
A Unix-style glob pattern to exclude files whose names match. Converted to regex internally. |
summary |
str |
None |
Path where the fylex.log file should be copied after the operation completes. If None, the log remains only in fylex.log in the current working directory. |
on_conflict |
str |
"rename" |
Defines how Fylex handles name collisions at the destination. See Conflict Resolution Modes below. |
max_workers |
int |
4 |
The maximum number of threads to use for concurrent file operations. Adjust based on your system's capabilities and disk I/O. |
verbose |
bool |
False |
If True, log messages will also be printed to the console in real-time. Otherwise, they are only written to fylex.log. |
recursive_check |
bool |
False |
If True, copy_files and move_files will scan source and destination subdirectories for matching files. Crucial for deep scans. |
has_extension |
bool |
False |
If True for copy_files and move_files, file filtering and duplicate checks will also consider the file extension in addition to size. Useful for more precise duplicate detection of files with identical sizes but different types. |
levels |
int |
-1 |
For spill, defines the maximum subdirectory depth to scan. -1 means infinite depth (all subdirectories). 0 means only the target directory itself (no subdirectories). 1 means immediate subdirectories, and so on. |
Conflict Resolution Modes (on_conflict)
Fylex offers smart handling of file name conflicts at the destination. The on_conflict parameter accepts one of the following string values:
"rename"(Default): If a file with the same name exists, the incoming file will be renamed (e.g.,document.txtbecomesdocument(1).txt,document(2).txt, etc.) to avoid overwriting."replace": The incoming file will unconditionally overwrite the existing file at the destination. The original file will be moved to a timestamped.fylex_deprecated/folder within the destination for safety."larger": The file with the larger file size will be kept. If the existing file is larger or equal, the incoming file is skipped. If skipped, the source file is moved tofylex.deprecated/. If the incoming file is larger, it replaces the existing one, and the original file is moved to.fylex_deprecated/."smaller": The file with the smaller file size will be kept. If the existing file is smaller or equal, the incoming file is skipped. If skipped, the source file is moved tofylex.deprecated/. If the incoming file is smaller, it replaces the existing one, and the original file is moved to.fylex_deprecated/."newer": The file with the more recent modification timestamp will be kept. If the existing file is newer or has the same timestamp, the incoming file is skipped. If skipped, the source file is moved tofylex.deprecated/. If the incoming file is newer, it replaces the existing one, and the original file is moved to.fylex_deprecated/."older": The file with the older modification timestamp will be kept. If the existing file is older or has the same timestamp, the incoming file is skipped. If skipped, the source file is moved tofylex.deprecated/. If the incoming file is older, it replaces the existing one, and the original file is moved to.fylex_deprecated/."skip": The incoming file will be skipped entirely if a file with the same name exists at the destination. The skipped source file is moved tofylex.deprecated/for review."prompt": Fylex will ask the user interactively (via console) whether to replace the existing file or skip the incoming one. If "replace" is chosen, the original file is moved to.fylex_deprecated/. If "skip" is chosen, the skipped source file is moved tofylex.deprecated/.
Examples
Let's assume the following directory structure for the examples:
/data/
├── project\_A/
│ ├── main.py
│ ├── config.ini
│ └── docs/
│ ├── readme.md
│ └── images/
│ └── img\_01.png
├── project\_B/
│ ├── index.html
│ └── style.css
├── temp/
│ ├── .tmp
│ ├── old\_data.bak
│ └── report.log
├── my\_files/
│ ├── photo.jpg
│ ├── document.pdf
│ └── sub\_folder/
│ └── nested\_file.txt
└── important\_notes.txt
And your destination directory is initially empty: /backup/
copy_files: Smart Copying
from fylex import copy_files
# Example 1: Copy all Python files from project_A to /backup, resolving conflicts by renaming.
# Only scans the top-level files of project_A if recursive_check=False
copy_files(src="/data/project_A", dest="/backup",
match_glob="*.py", on_conflict="rename", verbose=True)
# Result: /backup/main.py
# Example 2: Copy all files from /data/my_files including subdirectories,
# excluding .txt files, and keep the newer version on conflict.
# If a file like 'photo.jpg' exists in /backup/my_backup and is older,
# the existing 'photo.jpg' would be moved to '/backup/my_backup/.fylex_deprecated/YYYY-MM-DD_HH-MM-SS/'
# before the new 'photo.jpg' is copied.
copy_files(src="/data/my_files", dest="/backup/my_backup",
recursive_check=True, exclude_glob="*.txt", on_conflict="newer", verbose=True)
# Result: /backup/my_backup/photo.jpg, /backup/my_backup/document.pdf
# (nested_file.txt would be skipped due to exclusion)
# Example 3: Copy only 'important_notes.txt' from /data to /backup
copy_files(src="/data", dest="/backup",
match_names=["important_notes.txt"], verbose=True)
# Result: /backup/important_notes.txt
move_files: Smart Moving
move_files works identically to copy_files but deletes the source file after successful transfer.
from fylex import move_files
# Example: Move all .html and .css files from project_B to /web_files,
# prompting on conflict.
# If the user chooses to replace, the existing file in /web_files would be moved
# to '/web_files/.fylex_deprecated/YYYY-MM-DD_HH-MM-SS/'.
# If the user chooses to skip, the source file (e.g., /data/project_B/index.html)
# would be moved to 'fylex.deprecated/' (in the current working directory).
move_files(src="/data/project_B", dest="/web_files",
match_glob="*.{html,css}", on_conflict="prompt", interactive=True, verbose=True)
# User would be prompted for each file if it already exists in /web_files.
# After successful move: /data/project_B will no longer contain index.html or style.css
spill: Consolidating Files from Subdirectories
spill moves files from nested directories into the target root directory.
from fylex import spill
import os
import shutil
# Setup for spill example:
os.makedirs("/data/temp_spill/level1/level2", exist_ok=True)
with open("/data/temp_spill/fileA.txt", "w") as f: f.write("A")
with open("/data/temp_spill/level1/fileB.txt", "w") as f: f.write("B")
with open("/data/temp_spill/level1/level2/fileC.txt", "w") as f: f.write("C")
with open("/data/temp_spill/level1/level2/image.jpg", "w") as f: f.write("C")
# Example 1: Spill all files from subdirectories (infinite levels) into /data/temp_spill.
# If fileB.txt already existed in /data/temp_spill, it would be deprecated based on conflict mode.
spill(target="/data/temp_spill", levels=-1, verbose=True)
# Result: /data/temp_spill/fileA.txt, /data/temp_spill/fileB.txt, /data/temp_spill/fileC.txt, /data/temp_spill/image.jpg
# (fileA.txt is already at root, so not moved)
# The empty subdirectories /data/temp_spill/level1 and /data/temp_spill/level1/level2 will remain.
# Clean up for next example:
shutil.rmtree("/data/temp_spill")
os.makedirs("/data/temp_spill/level1/level2", exist_ok=True)
with open("/data/temp_spill/fileA.txt", "w") as f: f.write("A")
with open("/data/temp_spill/level1/fileB.txt", "w") as f: f.write("B")
with open("/data/temp_spill/level1/level2/fileC.txt", "w") as f: f.write("C")
# Example 2: Spill only files from immediate subdirectories (level 1), excluding .txt files.
spill(target="/data/temp_spill", levels=1, exclude_glob="*.txt", verbose=True)
# Result: Only files from /data/temp_spill/level1 (like fileB.txt if not excluded) would be considered.
# In this specific setup, since only .txt files are present, nothing would move.
# If image.jpg was in level1, it would move.
flatten: Flattening Directory Structures
flatten is ideal for taking a messy, deeply nested folder and putting all its files into one level, then cleaning up the empty folders.
from fylex import flatten
import os
import shutil
# Setup for flatten example (same as spill setup):
os.makedirs("/data/temp_flatten/level1/level2", exist_ok=True)
with open("/data/temp_flatten/fileX.log", "w") as f: f.write("X") # Will be ignored by default junk filter
with open("/data/temp_flatten/level1/fileY.jpg", "w") as f: f.write("Y")
with open("/data/temp_flatten/level1/level2/fileZ.pdf", "w") as f: f.write("Z")
# Example: Flatten the entire /data/temp_flatten directory.
# Any files in subdirectories that would overwrite an existing file in /data/temp_flatten
# would first cause the existing file to be moved to '/data/temp_flatten/.fylex_deprecated/'.
flatten(target="/data/temp_flatten", verbose=True)
# Result: /data/temp_flatten/fileX.log, /data/temp_flatten/fileY.jpg, /data/temp_flatten/fileZ.pdf
# After operation, /data/temp_flatten/level1/ and /data/temp_flatten/level1/level2/ will be deleted.
Handling Junk Files
Fylex comes with a predefined list of common "junk" file extensions and names. You can leverage this via the exclude_names and exclude_glob parameters or modify the JUNK_EXTENSIONS dictionary in the source.
from fylex import copy_files, JUNK_EXTENSIONS
# Combine all junk extensions and names into lists for exclusion
all_junk_extensions = [ext for sublist in JUNK_EXTENSIONS.values() for ext in sublist if ext.startswith(".")]
all_junk_names = [name for sublist in JUNK_EXTENSIONS.values() for name in sublist if not name.startswith(".")]
# Example: Copy all files from /data/temp to /archive, excluding all known junk.
# Note: You'd typically want to specify target directory for JUNK_EXTENSIONS if using.
# For simplicity, let's use common examples.
copy_files(src="/data/temp", dest="/archive",
exclude_glob="*.tmp", # Exclude temporary files
exclude_names=["thumbs.db", "desktop.ini"], # Exclude specific names
recursive_check=True, verbose=True)
# Result: .tmp, old_data.bak, report.log would be excluded based on these specific exclusions.
Dry Run and Interactive Modes
from fylex import copy_files
# Example: See what would happen if you were to copy all .txt files without actually doing it.
copy_files(src="/data/project_A", dest="/backup",
match_glob="*.md", dry_run=True, verbose=True)
# Output in log/console: "[DRY RUN] Would have copied: /data/project_A/docs/readme.md -> /backup/readme.md"
# No files are actually copied.
# Example: Be prompted for every action
copy_files(src="/data/project_A", dest="/backup",
match_glob="*.ini", interactive=True, verbose=True)
# Console: "Copy /data/project_A/config.ini to /backup/config.ini? [y/N]:"
# User input determines if the copy proceeds.
Working with Regex and Glob Patterns
Fylex allows you to combine regex and glob patterns for precise filtering.
from fylex import copy_files
# Example 1: Copy files that are either .jpg OR start with 'report'
copy_files(src="/data/my_files", dest="/backup",
match_regex=r".*\.jpg$", # Matches any .jpg
match_glob="report*", # Matches files starting with 'report'
verbose=True)
# Example 2: Exclude files that are either log files or contain 'temp' in their name
copy_files(src="/data/", dest="/filtered_data",
recursive_check=True,
exclude_regex=r".*\.log$",
exclude_glob="*temp*", # Matches files containing 'temp'
verbose=True)
5. Why Fylex is Superior
Compared to standard shell commands (cp, mv, rm, find, robocopy / rsync) or even basic scripting, Fylex offers significant advantages:
-
Intelligent Conflict Resolution (Beyond Overwrite/Rename) with Accident Prevention:
- Shell:
cp -foverwrites,cp -nskips.robocopyoffers more, but still lacks integrated safe-guards. - Fylex: Provides
rename,replace,larger,smaller,newer,older,skip, andprompt. Crucially, when Fylex replaces an existing file at the destination or skips a source file (due to a conflict), it first moves the affected file into a dedicated, timestamped.fylex_deprecated/folder. This virtually eliminates the risk of accidental data loss, allowing users to review and retrieve superseded or skipped files later. This safety net is a major leap beyond simple overwrite/skip options in other tools.
- Shell:
-
Built-in Data Integrity Verification (Hashing):
- OS commands perform a basic copy. You'd need to manually run
md5sumorsha256sumafter the copy and compare. Fylexusesxxhashfor fast post-copy verification, ensuring that the copied file is an exact, uncorrupted duplicate of the source. This is crucial for critical data.
- OS commands perform a basic copy. You'd need to manually run
-
Unified and Advanced Filtering:
findcombined withgrep,xargs, andegrepis powerful but often requires complex, multi-stage commands. Glob patterns are simpler but less flexible than regex.Fylexintegrates regex, glob, and exact name matching/exclusion directly into its functions, allowing for highly specific and readable filtering with a single API call.
-
Specialized Directory Reorganization (
spill,flatten):- Achieving "spill" or "flatten" with OS commands means chaining
find,mv,rmdir, and potentiallyxargswith very specific and often platform-dependent syntax. This is notoriously difficult to get right and can lead to accidental data loss if a mistake is made. Fylexprovides these as high-level, single-function operations with built-in safety (like dry run and empty directory cleanup), making them much safer and easier to use.
- Achieving "spill" or "flatten" with OS commands means chaining
-
Concurrency Out-of-the-Box:
- Basic OS commands are single-threaded. Parallelization requires advanced shell scripting with
xargs -Por similar, which adds complexity. Fylexautomatically utilizes aThreadPoolExecutorto process files concurrently, significantly boosting performance for large datasets without any extra effort from the user.
- Basic OS commands are single-threaded. Parallelization requires advanced shell scripting with
-
Comprehensive Logging & Dry Run Safety Net:
- OS commands typically dump output to stdout/stderr. Comprehensive logging requires redirection and parsing. Dry run is often simulated or requires specific flags that may not exist for all commands.
Fylexgenerates detailedfylex.logfor every operation, providing an auditable trail. Thedry_runmode is a built-in safeguard, allowing you to preview complex operations safely.
-
Python Integration & Extensibility:
- While powerful, shell scripts can be less maintainable and harder to integrate into larger software systems.
Fylex, being a Python library, is easily callable from any Python application, making it highly extensible and automatable within existing Python workflows.
-
User Interactivity:
- Shell: Limited options for user prompts during bulk operations.
Fylex:interactivemode provides a safety net by prompting for confirmation before each file transfer, giving you granular control.
In essence, Fylex transforms common, complex, and risky file management scenarios into straightforward, reliable, and efficient operations, saving time, preventing data loss, and simplifying automation.
6. Error Handling
Fylex implements robust error handling to ensure operations are performed safely and to provide clear feedback when issues arise.
InvalidPathError: Raised if a specified source path does not exist, or ifno_createisTrueand the destination path does not exist.PermissionDeniedError: Raised if Fylex lacks the necessary read or write permissions for a given path.ValueError: Raised for logical inconsistencies, such as trying to copy a directory into itself whenrecursive_checkis enabled.- Retry Mechanism: Transient errors during file copy/move operations are automatically retried up to
MAX_RETRIES(default: 5). If retries are exhausted, an error is logged.
7. Logging
Fylex provides detailed logging to fylex.log in the current working directory by default.
- INFO: Records successful operations, dry run simulations, and significant events, including deprecation actions.
- WARNING: Indicates potential issues, such as hash mismatches requiring retries.
- ERROR: Logs failures, permissions issues, or unhandled exceptions.
You can control log output:
verbose=True: Prints log messages to the console in real-time, in addition to the file.summary="path/to/my_log.log": Copies thefylex.logfile to the specified summary path upon completion.
8. Development & Contributing
Fylex is open to contributions! If you have ideas for new features, bug fixes, or improvements, feel free to:
- Fork the repository.
- Create a new branch (
git checkout -b feature/your-feature-name). - Make your changes.
- Write clear commit messages.
- Submit a Pull Request.
9. License
Fylex is released under the MIT License
xxHash used under BSD License
10. Author
Sivaprasad Murali — sivaprasad.off@gmail.com
Your files. Your rules. Just smarter.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 fylex-0.5.2.tar.gz.
File metadata
- Download URL: fylex-0.5.2.tar.gz
- Upload date:
- Size: 30.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f611857f93cae1fd4bc21d686702a08f6e3140fe5160519cc285fb2965e9122a
|
|
| MD5 |
dbadf5d3841a21c3012f76ea3bb5acfa
|
|
| BLAKE2b-256 |
b5b5735908341d90ae22aa4e97177204b92db592edf63c6a37e42c5b04dd5f4f
|
File details
Details for the file fylex-0.5.2-py3-none-any.whl.
File metadata
- Download URL: fylex-0.5.2-py3-none-any.whl
- Upload date:
- Size: 22.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77a7d03d1030a5f35b131a6ac373cb33b5980970b38090582b671d7e2e98bf16
|
|
| MD5 |
9de1e0f95cd93b7fdfafb1ccf56567c8
|
|
| BLAKE2b-256 |
b120d0c05ff642b6f66b994fcfc71094189c05f32f40511a22b805478d5f4140
|