================================================================================ PRACTICAL PYTHON SCRIPTING - CHALLENGE 1 SOLUTION Chapter 1: Paths & File System Navigation Challenge: Basic Path Finder ================================================================================ PROBLEM: Create a Python script that: 1. Uses __file__ to find the script's directory 2. Creates a path to a subdirectory called "data" using / 3. Checks if the "data" directory exists 4. If it exists, lists all files in it 5. If it doesn't exist, creates it SOLUTION: ================================================================================ from pathlib import Path # Step 1: Find the script's directory script_dir = Path(__file__).parent print(f"Script directory: {script_dir}") # Step 2: Create a path to the "data" subdirectory data_dir = script_dir / "data" print(f"Data directory: {data_dir}") # Step 3 & 4: Check if it exists and list files if data_dir.exists(): print(f"✅ Data directory exists") # List all files in the directory files = list(data_dir.glob("*")) if files: print(f" Files found: {len(files)}") for file in files: print(f" - {file.name}") else: print(" (directory is empty)") # Step 5: If it doesn't exist, create it else: print(f"❌ Data directory doesn't exist") data_dir.mkdir() print(f"✅ Created directory: {data_dir}") ================================================================================ HOW IT WORKS: ================================================================================ Step 1: Path(__file__).parent - __file__ = absolute path to this script - .parent = the directory containing this script - Result: /path/to/script_location/ Step 2: data_dir = script_dir / "data" - The / operator appends "data" to the path - Result: /path/to/script_location/data Step 3 & 4: if data_dir.exists() - .exists() returns True if directory exists, False otherwise - If True, use .glob("*") to find all files in the directory - .glob("*") returns a generator of all items (files and subdirectories) Step 5: data_dir.mkdir() - Creates the directory if it doesn't exist - Will raise an error if parent directories don't exist - (use mkdir(parents=True) to create parent directories too) ================================================================================ TESTING THE SOLUTION: ================================================================================ To test this script: 1. Create a file called "path_finder.py" and paste the solution above 2. Run it: python path_finder.py First run (data directory doesn't exist): Script directory: /home/philip/scripts Data directory: /home/philip/scripts/data ❌ Data directory doesn't exist ✅ Created directory: /home/philip/scripts/data 3. Run it again (data directory now exists): Script directory: /home/philip/scripts Data directory: /home/philip/scripts/data ✅ Data directory exists (directory is empty) 4. Add some files to the data/ directory and run again: Script directory: /home/philip/scripts Data directory: /home/philip/scripts/data ✅ Data directory exists Files found: 3 - file1.txt - file2.txt - file3.txt ================================================================================ WHAT EACH PART DOES: ================================================================================ from pathlib import Path - Import the Path class so we can use it Path(__file__).parent - __file__ = string containing path to this script - Path(...) = convert string to Path object - .parent = get the directory containing this file script_dir / "data" - / is the path joining operator - Creates a new Path: script_dir/data - Works on Windows and Linux automatically data_dir.exists() - Returns True if the path exists, False otherwise - Works for both files and directories data_dir.glob("*") - Find all files matching a pattern - "*" means "all files" - Returns an iterator (use list() to convert to a list) file.name - Get just the filename without the directory path - Example: /home/philip/data/file.txt → file.txt data_dir.mkdir() - Create a directory - Raises FileExistsError if it already exists (that's why we check first) - Raises FileNotFoundError if parent doesn't exist ================================================================================ ALTERNATIVE APPROACHES: ================================================================================ Approach 1: Using os.path (old way) import os script_dir = os.path.dirname(os.path.abspath(__file__)) data_dir = os.path.join(script_dir, "data") if os.path.exists(data_dir): files = os.listdir(data_dir) for file in files: print(file) else: os.mkdir(data_dir) # Works but more verbose and error-prone Approach 2: Using mkdir with parents=True from pathlib import Path script_dir = Path(__file__).parent data_dir = script_dir / "data" / "subdir" / "nested" # Create all parent directories automatically data_dir.mkdir(parents=True, exist_ok=True) # exist_ok=True means: don't error if it already exists Approach 3: More robust with error handling from pathlib import Path try: script_dir = Path(__file__).resolve().parent data_dir = script_dir / "data" if not data_dir.exists(): data_dir.mkdir() print(f"Created: {data_dir}") else: files = list(data_dir.glob("*")) print(f"Found {len(files)} items") except Exception as e: print(f"Error: {e}") ================================================================================ COMMON MISTAKES & HOW TO AVOID THEM: ================================================================================ ❌ MISTAKE 1: Forgetting to import Path data_dir = Path(__file__).parent / "data" # NameError: name 'Path' is not defined ✅ FIX: from pathlib import Path data_dir = Path(__file__).parent / "data" --- ❌ MISTAKE 2: Using + instead of / data_dir = Path(__file__).parent + "/data" # TypeError: unsupported operand type(s) for + ✅ FIX: data_dir = Path(__file__).parent / "data" --- ❌ MISTAKE 3: Not checking if directory exists before creating data_dir.mkdir() # FileExistsError: [Errno 17] File exists: '/path/to/data' ✅ FIX: if not data_dir.exists(): data_dir.mkdir() Or simply: data_dir.mkdir(parents=True, exist_ok=True) --- ❌ MISTAKE 4: Assuming __file__ is always absolute # Sometimes __file__ is relative, depending on how you run the script # python script.py → might be relative # python /full/path/script.py → will be absolute ✅ FIX: script_dir = Path(__file__).resolve().parent # .resolve() makes it absolute regardless ================================================================================ WHY THIS MATTERS IN REAL CODE: ================================================================================ This pattern appears everywhere in production scripts: 1. Database utility scripts need to find .env files 2. Web scrapers need to store downloaded files 3. Data processing scripts need output directories 4. Log files need consistent locations By using Path and __file__, your script works no matter where it's placed: - On Windows or Linux - In /var/www/myapp or /home/user/scripts - With relative or absolute paths - As a standalone script or imported module This is the foundation for writing portable, reliable Python utilities. ================================================================================ EDGE CASES TO CONSIDER: ================================================================================ Edge Case 1: What if parent directories don't exist? from pathlib import Path script_dir = Path(__file__).parent data_dir = script_dir / "data" / "nested" / "path" # This will fail if "data" doesn't exist: data_dir.mkdir() # FileNotFoundError: [Errno 2] No such file or directory # Use parents=True to create all directories: data_dir.mkdir(parents=True, exist_ok=True) --- Edge Case 2: What if __file__ is a symlink? from pathlib import Path # Symlink to the script: script_dir = Path(__file__).parent # Follows the symlink script_dir_real = Path(__file__).resolve().parent # Real location --- Edge Case 3: Permission denied from pathlib import Path script_dir = Path(__file__).parent data_dir = script_dir / "data" try: data_dir.mkdir() except PermissionError: print("Cannot create directory (permission denied)") ================================================================================ KEY TAKEAWAYS: ================================================================================ ✅ Always use Path from pathlib for file operations ✅ Use __file__ to find the script's location ✅ Use .parent to go up one directory level ✅ Use / to append directories to a path ✅ Use .exists() to check if something exists before creating it ✅ Use .mkdir() to create directories ✅ Use .glob() to find files matching a pattern ✅ Your scripts will work on Windows and Linux automatically The pattern you just learned is the foundation for all practical Python scripting. Everything else builds on this! ================================================================================