Paths & File System Navigation

Database Regeneration - Practical Scripting
Course 1 Β· Chapter 1 Β· Paths & File System Navigation

πŸ—‚οΈ Paths & File System Navigation

When you write a Python script that works with files, the biggest challenge isn't the logicβ€”it's figuring out where the files actually are. This chapter teaches the most important concept in practical Python scripting: how to reliably locate and work with files on any computer, in any directory, on any operating system.

⚠️ The Problem We're Solving

Imagine you write a script that reads HTML files. On your Windows computer, you test it:

with open("C:\\Users\\philip\\files\\chapters\\ts1-1.html") as f:
    content = f.read()

It works fine. You copy the script to your Linux web server and run it:

with open("C:\\Users\\philip\\files\\chapters\\ts1-1.html") as f:
    content = f.read()

It crashes. Why? Windows and Linux have different path formats, and the directories are different.

❌ COMMON MISTAKE: Hard-coding file paths. This makes your script work on one computer but nowhere else.

The solution: **make your script find files relative to where it's located**. This is what this chapter teaches.

✨ __file__: The Magic Variable

__file__ is a special Python variable that automatically contains the path to the current script. It's like Python saying "here's where I am right now."

What __file__ Contains

Create a test file called test.py with this code:

print(__file__)

On Windows: Running from C:\Users\philip\scripts\

C:\Users\philip\scripts\test.py

On Linux: Running from /home/philip/scripts/

/home/philip/scripts/test.py
βœ… WHAT IT DOES: __file__ is a string containing the absolute path to the Python script currently running. It automatically adapts to Windows or Linux path format.
πŸ€” WHY IT MATTERS: Your script now knows where it is. This is the foundation for finding other files relative to its location.

πŸ“¦ The pathlib Module and Path Objects

Python's pathlib module provides a better way to work with file paths. Instead of string manipulation (which is error-prone), you use Path objects that handle Windows/Linux differences automatically.

Creating a Path Object

from pathlib import Path

# Convert __file__ to a Path object
script_path = Path(__file__)
print(script_path)
βœ… WHAT IT DOES: Path(__file__) converts the string path into a Path object. Now you can call methods on it.
πŸ€” WHY USE Path INSTEAD OF STRINGS:
  • Path handles Windows backslashes vs Linux forward slashes automatically
  • Path has useful methods like .parent, .exists(), .open()
  • Path prevents errors from mixing path separators

Alternative: os.path (Old Way)

import os

script_path = os.path.abspath(__file__)
script_dir = os.path.dirname(script_path)
Old Way (os.path)
import os
import os.path

chapters_dir = os.path.join(
    os.path.dirname(__file__),
    "courses",
    "html"
)
New Way (pathlib)
from pathlib import Path

chapters_dir = Path(__file__).parent / "courses" / "html"
πŸ’‘ Modern Recommendation

Always use pathlib.Path for new code. It's cleaner, safer, and more readable. The old os.path module still works, but pathlib is the modern standard.

πŸ‘† The .parent Property: Going Up Directories

The .parent property gets the directory that contains a file or directory. You can chain multiple .parent calls to go up multiple levels.

Understanding Parent Step-by-Step

Let's say your script is at:

/var/www/myapp/rebuild_typescript_course.py

Here's what .parent does at each step:

Path Traversal with .parent

Stage 1:
Path(__file__)
Result:
/var/www/myapp/rebuild_typescript_course.py

This is the script file itself.

Stage 2:
Path(__file__).parent
Result:
/var/www/myapp

Go up one level to the directory containing the script.

Stage 3:
Path(__file__).parent.parent
Result:
/var/www

Go up two levels.

Stage 4:
Path(__file__).parent.parent.parent
Result:
/var

Go up three levels.

βœ… WHAT IT DOES: Each .parent goes up one directory level. You can chain them: .parent.parent.parent goes up three levels.
πŸ€” WHY THIS MATTERS: Your script doesn't know where it will be placed. By using .parent, your script can find files relative to its own location, regardless of where it's copied to.

πŸ”¨ Building Paths with the / Operator

Once you have a directory, you add subdirectories and filenames using the / operator (the slash character). This is much cleaner than string concatenation.

The / Operator for Paths

from pathlib import Path

# Start from the script's directory
script_dir = Path(__file__).parent

# Build a path step by step
chapters_dir = script_dir / "courses" / "Programming" / "TypeScript" / "html"

# Get a specific file
chapter_file = chapters_dir / "ts1-1.html"

print(chapter_file)
# Output: /var/www/myapp/courses/Programming/TypeScript/html/ts1-1.html
βœ… WHAT IT DOES: The / operator appends a subdirectory or filename to a path. It works on Windows and Linux automatically.
πŸ€” WHY USE / INSTEAD OF STRING CONCATENATION:
  • βœ… script_dir / "chapters" β€” Clean and readable
  • ❌ script_dir + "\chapters" β€” Breaks on Linux (needs forward slash)
  • ❌ script_dir + "/" + "chapters" β€” Verbose and fragile

Real Example: rebuild_typescript_course.py

Here's how the actual rebuild script finds chapters:

from pathlib import Path

# The script is at: /var/www/myapp/rebuild_typescript_course.py
# We want chapters at: /var/www/myapp/courses/Programming/TypeScript/html/

CHAPTERS_DIR = Path(__file__).parent / "courses" / "Programming" / "TypeScript" / "html"

# Now we can use it:
html_files = list(CHAPTERS_DIR.glob("ts1-*.html"))
for html_file in html_files:
    print(f"Found: {html_file}")

βœ… Checking If Files Exist

The .exists() Method

from pathlib import Path

file_path = Path("/var/www/myapp/courses/TypeScript/html/ts1-1.html")

if file_path.exists():
    print("βœ… File found!")
else:
    print("❌ File not found!")
βœ… WHAT IT DOES: .exists() returns True if the file or directory exists, False otherwise.

Other Useful Methods

from pathlib import Path

file_path = Path("/var/www/myapp/courses/ts1-1.html")

# Is it a file (not a directory)?
if file_path.is_file():
    print("It's a file")

# Is it a directory (not a file)?
if file_path.is_dir():
    print("It's a directory")

# Get just the filename without the directory
print(file_path.name)  # Output: ts1-1.html

# Get the file extension
print(file_path.suffix)  # Output: .html

# Get the filename without extension
print(file_path.stem)  # Output: ts1-1

πŸ–₯️ Windows vs Linux: Path Formats

The Path Format Problem

Windows uses backslashes:

C:\Users\philip\files\chapters\ts1-1.html

Linux uses forward slashes:

/home/philip/files/chapters/ts1-1.html

If you hard-code Windows paths, your script breaks on Linux. If you hard-code Linux paths, your script breaks on Windows.

βœ… THE SOLUTION: Use pathlib.Path. It automatically uses the correct separator for your operating system.
from pathlib import Path

# This works the same way on Windows and Linux!
chapters_dir = Path(__file__).parent / "courses" / "html"

# On Windows, it becomes: C:\path\to\courses\html
# On Linux, it becomes: /path/to/courses/html
print(chapters_dir)

⚠️ Common Mistakes & How to Fix Them

❌ MISTAKE 1: Forgetting to import Path
# This will crash!
chapters_dir = Path(__file__).parent / "courses"
# NameError: name 'Path' is not defined
Fix:
from pathlib import Path  # ← Add this!

chapters_dir = Path(__file__).parent / "courses"
❌ MISTAKE 2: Using + instead of / to combine paths
# This doesn't work as expected!
chapters_dir = Path(__file__).parent + "/courses"
# TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str'
Fix:
chapters_dir = Path(__file__).parent / "courses"  # ← Use /
❌ MISTAKE 3: Assuming __file__ is always absolute

In some cases (like running with python -m), __file__ might be relative.

Fix:
from pathlib import Path

# Make sure it's absolute
script_dir = Path(__file__).resolve().parent

🎯 Edge Cases & Special Situations

Edge Case 1: File Doesn't Exist Yet

If you're creating a new file, the path won't exist, but that's OK:

from pathlib import Path

file_path = Path(__file__).parent / "new_file.txt"

if not file_path.exists():
    with open(file_path, "w") as f:
        f.write("Hello")
Edge Case 2: Symbolic Links (Shortcuts)

.resolve() follows symbolic links to the actual file:

from pathlib import Path

# If __file__ is a symlink, follow it to the real file
real_script = Path(__file__).resolve()
Edge Case 3: Relative Paths from Current Directory

Sometimes you want files relative to where the user ran the command, not where the script is:

from pathlib import Path

# User's current working directory
current_dir = Path(".").resolve()

# Script's directory
script_dir = Path(__file__).resolve().parent

🌍 Real-World Example: rebuild_typescript_course.py

Here's how the concepts in this chapter are used in actual production code:

from pathlib import Path

# This script is at: /var/www/myapp/rebuild_typescript_course.py

# Start from the script's location
SCRIPT_DIR = Path(__file__).resolve().parent

# Build the path to chapters folder relative to the script
CHAPTERS_DIR = SCRIPT_DIR / "courses" / "Programming" / "TypeScript" / "html"

# Check if it exists
if not CHAPTERS_DIR.exists():
    print(f"❌ Chapters directory not found: {CHAPTERS_DIR}")
    exit(1)

# Find all ts1-*.html files
html_files = sorted(CHAPTERS_DIR.glob("ts1-*.html"))

for html_file in html_files:
    print(f"πŸ“„ Processing: {html_file.name}")
    with open(html_file, "r") as f:
        content = f.read()
        print(f"   Read {len(content)} bytes")

πŸ’» Coding Challenge

Challenge: Create a Path Finder Script

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

Goal: Practice using Path, .parent, .exists(), and the / operator.

β†’ Solution

🎯 What's Next

Now that you can locate files reliably, the next chapter covers Configuration & Environment Variables β€” how to load settings like database credentials from .env files so your scripts can adapt to different environments.