如何在Python开发的MyFancyApp中实现.gitignore式文件忽略打包功能?
How to Build a Python CLI Tool with .gitignore-Style Ignore Rules
Absolutely! You can totally build a .gitignore-style exclusion system for your MyFancyApp CLI tool. The build command will traverse your current directory, skip files/dirs listed in .MyFancyAppIgnore, and package everything else into a ZIP file. Let's walk through how to do this step by step.
Core Approach
We'll split this into three key tasks:
- Parse the
.MyFancyAppIgnorefile to extract exclusion rules (handling comments, empty lines, and wildcards like*,**,?). - Traverse the directory tree and filter out any paths that match the ignore rules.
- Package the remaining files into a ZIP archive.
- Wrap everything in a CLI using a library like
click(cleaner than argparse for CLI tools).
Step-by-Step Implementation
1. Install Dependencies (Optional but Recommended)
We'll use click for the CLI interface. Install it via pip:
pip install click
2. Full Code Example
Create a file named MyFancyApp.py with the following code:
import click import zipfile from pathlib import Path import fnmatch def load_ignore_rules(ignore_file_path): """Load and parse the .MyFancyAppIgnore file.""" rules = [] if not ignore_file_path.exists(): return rules with open(ignore_file_path, 'r') as f: for line in f: line = line.strip() # Skip comments and empty lines if not line or line.startswith('#'): continue # Store the rule (we'll handle path matching later) rules.append(line) return rules def should_ignore(path, ignore_rules, root_dir): """Check if a given path should be ignored based on the rules.""" # Convert path to relative path from root directory relative_path = str(path.relative_to(root_dir)) # Check each rule against the relative path for rule in ignore_rules: # Handle recursive wildcards (**) if '**' in rule: # Split rule into parts before and after ** prefix, suffix = rule.split('**', 1) if (fnmatch.fnmatch(relative_path, f"{prefix}*{suffix}") or fnmatch.fnmatch(relative_path, f"{prefix}*{suffix}/")): return True # Handle standard wildcards and exact matches elif fnmatch.fnmatch(relative_path, rule) or fnmatch.fnmatch(relative_path, f"{rule}/"): return True # Also exclude the ignore file itself and the output ZIP to avoid recursion if path == root_dir / '.MyFancyAppIgnore': return True return False def build_project(root_dir, output_zip): """Traverse directory, filter files, and create ZIP archive.""" ignore_file = root_dir / '.MyFancyAppIgnore' ignore_rules = load_ignore_rules(ignore_file) with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zipf: # Walk through all files and directories for path in root_dir.rglob('*'): if should_ignore(path, ignore_rules, root_dir): continue # Add to ZIP: if it's a directory, add an entry (to preserve structure) if path.is_dir(): # Add trailing slash to denote directory in ZIP zipf.write(path, arcname=str(path.relative_to(root_dir)) + '/') else: zipf.write(path, arcname=str(path.relative_to(root_dir))) click.echo(f"Successfully built {output_zip}!") @click.group() def cli(): """MyFancyApp: A fancy CLI tool for packaging your project.""" pass @cli.command() @click.option('--output', '-o', default='MyFancyApp.zip', help='Output ZIP file name.') @click.option('--dir', default='.', help='Directory to package (default: current directory).') def build(output, dir): """Package the project into a ZIP file, respecting .MyFancyAppIgnore rules.""" root_dir = Path(dir).resolve() output_zip = Path(output).resolve() build_project(root_dir, output_zip) if __name__ == '__main__': cli()
3. How It Works
- Rule Parsing: The
load_ignore_rulesfunction reads.MyFancyAppIgnore, skips comments and empty lines, and stores valid exclusion rules. - Path Filtering:
should_ignoreconverts each path to a relative path (so rules work correctly) and checks against wildcards usingfnmatch. It handles recursive patterns like**/node_modulesto ignore allnode_modulesdirectories anywhere in the tree. - ZIP Packaging:
build_projectuseszipfileto create a compressed ZIP, adding only non-ignored files and preserving directory structure. - CLI Interface: The
clickdecorators turn the functions into a proper CLI, so you can runMyFancyApp buildwith optional flags for output path and target directory.
4. Using the Tool
- Create a
.MyFancyAppIgnorefile in your project directory with rules like:# Ignore virtual environments venv/ __pycache__/ # Ignore log files *.log # Ignore all temp directories anywhere **/temp/ - Run the build command:
Or specify a custom output:python MyFancyApp.py buildpython MyFancyApp.py build -o my_project.zip
Optional Enhancements
- Support Negation Rules: Add support for
!prefix to negate rules (e.g.,!important.logto include a file that would otherwise be ignored). This requires adjusting theshould_ignorefunction to handle priority of negation rules. - Use a Gitignore Parser Library: For full compatibility with
.gitignoresyntax (including edge cases), use thegitignore_parserlibrary. Install it withpip install gitignore-parserand replace the rule loading and checking logic with:
This handles all standardfrom gitignore_parser import parse_gitignore def load_ignore_rules(ignore_file_path): if not ignore_file_path.exists(): return lambda path: False return parse_gitignore(ignore_file_path) def should_ignore(path, ignore_func, root_dir): return ignore_func(path) or path == root_dir / '.MyFancyAppIgnore'.gitignorefeatures out of the box.
内容的提问来源于stack exchange,提问作者Tho
相关产品推荐
相关产品推荐

