From fb9f3217e2de47b7b53d4e9ff6d2cc4ef100036c Mon Sep 17 00:00:00 2001 From: Jiang Date: Mon, 16 Mar 2026 15:06:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B8=85=E7=90=86=20pycache?= =?UTF-8?q?=20=E5=92=8C=E7=BC=96=E8=AF=91=E6=89=A9=E5=B1=95=E7=9A=84?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/clean_pycache.py | 48 +++++++++++++++++++ scripts/compile.py | 101 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 scripts/clean_pycache.py diff --git a/scripts/clean_pycache.py b/scripts/clean_pycache.py new file mode 100644 index 0000000..56fab63 --- /dev/null +++ b/scripts/clean_pycache.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +import os +import shutil +import sys + +def clean_pycache(root_dir): + """ + Recursively deletes all __pycache__ directories and .pyc files. + """ + print(f"Cleaning __pycache__ in: {root_dir}") + for root, dirs, files in os.walk(root_dir): + # Delete __pycache__ directories + if "__pycache__" in dirs: + pycache_path = os.path.join(root, "__pycache__") + print(f"Removing directory: {pycache_path}") + shutil.rmtree(pycache_path) + dirs.remove("__pycache__") # Don't visit the deleted directory + + # Delete orphan .pyc files + for file in files: + if file.endswith(".pyc"): + pyc_path = os.path.join(root, file) + print(f"Removing file: {pyc_path}") + os.remove(pyc_path) + +def clean_extensions(root_dir): + """ + Recursively deletes all compiled .so and .pyd files. + """ + print(f"Cleaning compiled extensions in: {root_dir}") + for root, dirs, files in os.walk(root_dir): + for file in files: + if file.endswith(".so") or file.endswith(".pyd"): + ext_path = os.path.join(root, file) + print(f"Removing extension: {ext_path}") + os.remove(ext_path) + +if __name__ == "__main__": + project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + + # Clean pycache + clean_pycache(project_root) + + # Optionally clean extensions if requested via argument + if len(sys.argv) > 1 and sys.argv[1] == "--all": + clean_extensions(project_root) + + print("Cleanup complete.") diff --git a/scripts/compile.py b/scripts/compile.py index b8547e6..84339e8 100755 --- a/scripts/compile.py +++ b/scripts/compile.py @@ -6,6 +6,83 @@ from setuptools import setup, Extension from Cython.Build import cythonize +def delete_source_files(target_dirs): + """ + Deletes the original .py files in the target directories after successful compilation. + Caution: This will remove your source code! + """ + if isinstance(target_dirs, str): + target_dirs = [target_dirs] + + project_root = os.getcwd() + print(f"Deleting source .py files in: {target_dirs}") + + for target_dir in target_dirs: + if not os.path.exists(target_dir): + continue + + abs_target_path = os.path.abspath(target_dir) + + if os.path.isfile(abs_target_path): + if abs_target_path.endswith(".py") and os.path.abspath( + abs_target_path + ) != os.path.abspath(__file__): + print(f"Deleting source: {abs_target_path}") + os.remove(abs_target_path) + continue + + # If it's a directory, walk through it + for root, dirs, files in os.walk(abs_target_path): + for file in files: + if file.endswith(".py"): + file_path = os.path.join(root, file) + # Skip this script itself + if os.path.abspath(file_path) == os.path.abspath(__file__): + continue + # Skip __init__.py if you want to keep package structure, + # but usually Cython can handle them too. + print(f"Deleting source: {file_path}") + os.remove(file_path) + + +def clean_extensions(target_dirs): + """ + Deletes all compiled .so/.pyd files in the target directories. + """ + if isinstance(target_dirs, str): + target_dirs = [target_dirs] + + project_root = os.getcwd() + print(f"Cleaning compiled files in: {target_dirs}") + + for target_dir in target_dirs: + if not os.path.exists(target_dir): + continue + + abs_target_path = os.path.abspath(target_dir) + + if os.path.isfile(abs_target_path): + # If it's a .py file, look for its .so/.pyd counterpart + dir_name = os.path.dirname(abs_target_path) + base_name = os.path.splitext(os.path.basename(abs_target_path))[0] + for file in os.listdir(dir_name): + if file.startswith(base_name) and ( + file.endswith(".so") or file.endswith(".pyd") + ): + file_path = os.path.join(dir_name, file) + print(f"Removing: {file_path}") + os.remove(file_path) + continue + + # If it's a directory, walk through it + for root, dirs, files in os.walk(abs_target_path): + for file in files: + if file.endswith(".so") or file.endswith(".pyd"): + file_path = os.path.join(root, file) + print(f"Removing: {file_path}") + os.remove(file_path) + + def build_extensions(target_dirs): """ Compiles all .py files in the target directories (recursively) into .so/.pyd extensions. @@ -119,17 +196,35 @@ if __name__ == "__main__": # Check for help flag if len(sys.argv) > 1 and sys.argv[1] in ["--help", "-h"]: - print("Usage: python scripts/build_extensions.py [directory1] [directory2] ...") + print( + "Usage: python scripts/build_extensions.py [--clean|--delete-source] [directory1] [directory2] ..." + ) print( "Compiles all Python files in the given directories into C extensions (.so/.pyd)." ) + print("Use --clean to remove existing compiled extensions.") + print("Use --delete-source to remove source .py files after compilation.") print(f"Default directories if none provided: {DEFAULT_TARGETS}") sys.exit(0) + clean_mode = False + delete_source_mode = False if len(sys.argv) > 1: - target_directories = sys.argv[1:] + if sys.argv[1] == "--clean": + clean_mode = True + target_directories = sys.argv[2:] + elif sys.argv[1] == "--delete-source": + delete_source_mode = True + target_directories = sys.argv[2:] + else: + target_directories = sys.argv[1:] else: print(f"No directories specified. Using defaults: {DEFAULT_TARGETS}") target_directories = DEFAULT_TARGETS - build_extensions(target_directories) + if clean_mode: + clean_extensions(target_directories) + elif delete_source_mode: + delete_source_files(target_directories) + else: + build_extensions(target_directories)