Skip to content

Instantly share code, notes, and snippets.

@mrichman
Created December 31, 2025 18:18
Show Gist options
  • Select an option

  • Save mrichman/b5b0ee7bf84d51592ab93c3a9cc38e9d to your computer and use it in GitHub Desktop.

Select an option

Save mrichman/b5b0ee7bf84d51592ab93c3a9cc38e9d to your computer and use it in GitHub Desktop.
The script detects when you rename a file's extension (e.g., right-click → rename data.xlsx to data.csv) and converts the content to match.
import os
import time
import shutil
from pathlib import Path
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import pandas as pd
class ExtensionChangeHandler(FileSystemEventHandler):
def __init__(self, watch_dir):
self.watch_dir = Path(watch_dir)
self.processing = set()
def on_moved(self, event):
"""Triggered when a file is renamed (extension changed)"""
if event.is_directory:
return
src_path = Path(event.src_path)
dest_path = Path(event.dest_path)
# Check if only extension changed (same base name)
if src_path.stem == dest_path.stem and src_path.suffix != dest_path.suffix:
if dest_path in self.processing:
return
self.processing.add(dest_path)
time.sleep(0.2)
self.convert_file(src_path.suffix, dest_path)
self.processing.discard(dest_path)
def convert_file(self, original_ext, target_path):
"""Convert file content from original format to target format"""
original_ext = original_ext.lower()
target_ext = target_path.suffix.lower()
# Create backup with original extension
backup_path = target_path.with_suffix(original_ext + '.backup')
try:
# Copy to backup (file still has old content but new extension)
shutil.copy2(target_path, backup_path)
print(f"Converting {target_path.name} from {original_ext} to {target_ext}")
# Read from backup with original format
df = None
if original_ext in ['.xlsx', '.xls']:
df = pd.read_excel(backup_path)
elif original_ext == '.csv':
df = pd.read_csv(backup_path)
elif original_ext == '.json':
df = pd.read_json(backup_path)
elif original_ext == '.txt':
df = pd.read_csv(backup_path, sep='\t')
else:
print(f"✗ Unsupported source format: {original_ext}")
backup_path.unlink()
return
# Write to target with new format
if target_ext == '.csv':
df.to_csv(target_path, index=False)
elif target_ext in ['.xlsx', '.xls']:
df.to_excel(target_path, index=False, engine='openpyxl')
elif target_ext == '.json':
df.to_json(target_path, orient='records', indent=2)
elif target_ext == '.txt':
df.to_csv(target_path, sep='\t', index=False)
else:
print(f"✗ Unsupported target format: {target_ext}")
# Restore original
shutil.move(backup_path, target_path)
return
# Remove backup after successful conversion
backup_path.unlink()
print(f"✓ Converted {target_path.name}")
except Exception as e:
print(f"✗ Error converting {target_path.name}: {str(e)}")
# Restore original file on error
if backup_path.exists():
shutil.move(backup_path, target_path)
def main():
watch_directory = "./watched_files"
Path(watch_directory).mkdir(exist_ok=True)
print(f"Watching directory: {watch_directory}")
print("Rename a file's extension to trigger conversion")
print("Supported formats: .xlsx, .xls, .csv, .json, .txt")
print("Press Ctrl+C to stop\n")
event_handler = ExtensionChangeHandler(watch_directory)
observer = Observer()
observer.schedule(event_handler, watch_directory, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
print("\nStopped watching")
observer.join()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment