VBAtoPython
← All articles

Filesystemobject to Pathlib

If you've been working with VBA for file and folder operations, you're probably familiar with the FileSystemObject (FSO) — that trusty COM object that...

If you've been working with VBA for file and folder operations, you're probably familiar with the FileSystemObject (FSO) — that trusty COM object that handles everything from checking if files exist to creating directories and reading file properties. When making the transition from filesystemobject to pathlib in Python, you'll discover a more elegant, cross-platform approach that's both more powerful and easier to read. Python's pathlib module, introduced in Python 3.4, represents the modern way to handle file system operations with an object-oriented interface that will feel natural once you understand the core concepts.

The shift from VBA's FileSystemObject to Python's pathlib isn't just about learning new syntax — it's about embracing a more robust, platform-independent approach to file handling that eliminates many of the pain points you've likely encountered in VBA development.

Why Make the Switch from FileSystemObject to Pathlib?

Before diving into specific examples, it's worth understanding why pathlib represents such a significant improvement over VBA's FileSystemObject approach.

Object-Oriented Path Handling

In VBA, you typically work with file paths as strings, using the FileSystemObject to perform operations on those string paths. Python's pathlib treats paths as objects with built-in methods, making code more readable and less error-prone.

Cross-Platform Compatibility

While VBA's FileSystemObject is Windows-centric, pathlib automatically handles path separators and conventions across Windows, macOS, and Linux. This means your Python code will work seamlessly across different operating systems without modification.

Better Error Handling

Pathlib provides more specific exceptions and clearer error messages, making debugging easier compared to the sometimes cryptic error messages from FileSystemObject operations.

Modern Python Integration

Pathlib integrates seamlessly with other Python libraries and follows Python's modern conventions, making your code more maintainable and easier for other Python developers to understand.

Basic Path Operations: From FSO to Pathlib

Let's start with the most fundamental operations you'll need when converting from filesystemobject to pathlib.

Creating and Working with Paths

In VBA, you typically construct file paths by concatenating strings or using the FileSystemObject.BuildPath method:

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

' Constructing paths in VBA
Dim filePath As String
filePath = fso.BuildPath("C:\Users\John\Documents", "report.xlsx")

' Or with string concatenation (error-prone)
filePath = "C:\Users\John\Documents\" & "report.xlsx"

Python's pathlib makes this cleaner and more reliable:

from pathlib import Path

# Constructing paths in Python - multiple approaches
file_path = Path("C:/Users/John/Documents") / "report.xlsx"

# Alternative approaches
file_path = Path("C:/Users/John/Documents/report.xlsx")
file_path = Path("C:", "Users", "John", "Documents", "report.xlsx")

print(file_path)  # Automatically uses correct separators for OS

The Python approach is superior because the / operator automatically handles path separators correctly for the current operating system, and you don't need to worry about trailing slashes or backslashes.

Checking if Files and Directories Exist

File existence checking is one of the most common operations you'll migrate:

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

If fso.FileExists("C:\Users\John\Documents\report.xlsx") Then
    Debug.Print "File exists"
End If

If fso.FolderExists("C:\Users\John\Documents") Then
    Debug.Print "Folder exists"
End If

Python pathlib simplifies this with intuitive method names:

from pathlib import Path

file_path = Path("C:/Users/John/Documents/report.xlsx")
folder_path = Path("C:/Users/John/Documents")

if file_path.exists():
    print("File exists")

if folder_path.exists():
    print("Folder exists")

# More specific checks
if file_path.is_file():
    print("It's definitely a file")

if folder_path.is_dir():
    print("It's definitely a directory")

The advantage here is that pathlib provides more specific methods (is_file(), is_dir()) that help you write more precise code, and the method names are more intuitive than remembering separate FileExists vs FolderExists methods.

File Information and Properties

Getting file information is another area where the transition from filesystemobject to pathlib shows clear benefits.

File Size, Dates, and Attributes

VBA requires multiple FileSystemObject calls to get comprehensive file information:

Dim fso As Object
Dim file As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.GetFile("C:\Users\John\Documents\report.xlsx")

Debug.Print "Size: " & file.Size
Debug.Print "Date Created: " & file.DateCreated
Debug.Print "Date Modified: " & file.DateLastModified
Debug.Print "Attributes: " & file.Attributes

Python pathlib provides a more streamlined approach:

from pathlib import Path
import datetime

file_path = Path("C:/Users/John/Documents/report.xlsx")

if file_path.exists():
    # File size
    size = file_path.stat().st_size
    print(f"Size: {size} bytes")
    
    # Modification time
    mod_time = datetime.datetime.fromtimestamp(file_path.stat().st_mtime)
    print(f"Modified: {mod_time}")
    
    # Creation time (Windows) or metadata change time (Unix)
    create_time = datetime.datetime.fromtimestamp(file_path.stat().st_ctime)
    print(f"Created: {create_time}")
    
    # More convenient methods for common operations
    print(f"File name: {file_path.name}")
    print(f"File extension: {file_path.suffix}")
    print(f"Parent directory: {file_path.parent}")

The Python approach gives you more control and better integration with Python's datetime handling, plus convenient properties like name, suffix, and parent that eliminate the need for string manipulation.

Directory Operations

Working with directories is where you'll really appreciate the power of pathlib compared to VBA's FileSystemObject.

Creating Directories

VBA directory creation:

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

' Create a single directory
If Not fso.FolderExists("C:\Users\John\NewProject") Then
    fso.CreateFolder("C:\Users\John\NewProject")
End If

' Creating nested directories requires multiple calls or error handling

Python pathlib handles this more elegantly:

from pathlib import Path

# Create a single directory
new_dir = Path("C:/Users/John/NewProject")
new_dir.mkdir(exist_ok=True)  # Won't error if directory already exists

# Create nested directories in one call
nested_dir = Path("C:/Users/John/Projects/2024/Q1/Reports")
nested_dir.mkdir(parents=True, exist_ok=True)

The parents=True parameter is particularly powerful — it creates all intermediate directories automatically, something that requires multiple steps or complex error handling in VBA.

Listing Directory Contents

Iterating through directory contents shows another major advantage of pathlib:

Dim fso As Object
Dim folder As Object
Dim file As Object
Set fso = CreateObject("Scripting.FileSystemObject")
Set folder = fso.GetFolder("C:\Users\John\Documents")

' List all files
For Each file In folder.Files
    Debug.Print file.Name
Next file

' List all subfolders
Dim subfolder As Object
For Each subfolder In folder.SubFolders
    Debug.Print subfolder.Name
Next subfolder

Python pathlib provides multiple elegant ways to iterate:

from pathlib import Path

docs_path = Path("C:/Users/John/Documents")

# List all files and directories
for item in docs_path.iterdir():
    print(item.name)

# List only files
for file_path in docs_path.iterdir():
    if file_path.is_file():
        print(f"File: {file_path.name}")

# List only directories
for dir_path in docs_path.iterdir():
    if dir_path.is_dir():
        print(f"Directory: {dir_path.name}")

# Recursive listing (powerful!)
for excel_file in docs_path.rglob("*.xlsx"):
    print(f"Found Excel file: {excel_file}")

# Filter by pattern
for python_file in docs_path.glob("*.py"):
    print(f"Python file: {python_file}")

The glob() and rglob() methods are particularly powerful, providing pattern matching that would require complex VBA code to achieve.

File Operations

Moving, copying, and deleting files showcase more advantages of the filesystemobject to pathlib migration.

Moving and Copying Files

VBA file operations:

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

' Move a file
fso.MoveFile "C:\Users\John\Documents\report.xlsx", "C:\Users\John\Archive\report.xlsx"

' Copy a file
fso.CopyFile "C:\Users\John\Documents\template.xlsx", "C:\Users\John\NewProject\template.xlsx"

' Delete a file
fso.DeleteFile "C:\Users\John\Temp\old_report.xlsx"

Python pathlib combined with the shutil module:

from pathlib import Path
import shutil

# Move a file (rename also works for moving)
source = Path("C:/Users/John/Documents/report.xlsx")
destination = Path("C:/Users/John/Archive/report.xlsx")

# Ensure destination directory exists
destination.parent.mkdir(parents=True, exist_ok=True)
source.rename(destination)

# Copy a file
source = Path("C:/Users/John/Documents/template.xlsx")
destination = Path("C:/Users/John/NewProject/template.xlsx")
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)  # copy2 preserves metadata

# Delete a file
old_file = Path("C:/Users/John/Temp/old_report.xlsx")
if old_file.exists():
    old_file.unlink()  # unlink() is the pathlib way to delete files

While Python requires an additional import (shutil), the operations are more explicit and the automatic directory creation makes the code more robust.

Advanced Path Manipulation

One area where pathlib really shines compared to FileSystemObject is in path manipulation and analysis.

Working with Path Components

VBA path manipulation often involves string operations:

Dim fso As Object
Dim fullPath As String
Set fso = CreateObject("Scripting.FileSystemObject")
fullPath = "C:\Users\John\Documents\Reports\Q1\sales_report.xlsx"

Debug.Print "File name: " & fso.GetFileName(fullPath)
Debug.Print "Parent folder: " & fso.GetParentFolderName(fullPath)
Debug.Print "Base name: " & fso.GetBaseName(fullPath)
Debug.Print "Extension: " & fso.GetExtensionName(fullPath)

Python pathlib makes this much more intuitive:

from pathlib import Path

file_path = Path("C:/Users/John/Documents/Reports/Q1/sales_report.xlsx")

print(f"File name: {file_path.name}")  # sales_report.xlsx
print(f"Parent directory: {file_path.parent}")  # C:/Users/John/Documents/Reports/Q1
print(f"Stem (name without extension): {file_path.stem}")  # sales_report
print(f"Extension: {file_path.suffix}")  # .xlsx
print(f"All extensions: {file_path.suffixes}")  # ['.xlsx'] (useful for .tar.gz etc.)

# Path parts as a tuple
print(f"Path parts: {file_path.parts}")

# Navigate up the directory tree
print(f"Grandparent: {file_path.parent.parent}")

# Check if path is absolute or relative
print(f"Is absolute: {file_path.is_absolute()}")

The object-oriented approach eliminates the need to remember multiple function names and makes the code self-documenting.

Error Handling and Best Practices

When transitioning from filesystemobject to pathlib, it's important to understand the different error handling approaches.

VBA Error Handling

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

On Error GoTo ErrorHandler
    fso.CopyFile "source.xlsx", "destination.xlsx"
    Debug.Print "File copied successfully"
    Exit Sub

ErrorHandler:
    Debug.Print "Error: " & Err.Description

Python Exception Handling

from pathlib import Path
import shutil

try:
    source = Path("source.xlsx")
    destination = Path("destination.xlsx")
    
    if not source.exists():
        raise FileNotFoundError(f"Source file {source} does not exist")
    
    shutil.copy2(source, destination)
    print("File copied successfully")
    
except FileNotFoundError as e:
    print(f"File not found: {e}")
except PermissionError as e:
    print(f"Permission denied: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

Python's exception system provides more specific error types, making it easier to handle different scenarios appropriately.

Real-World Example: File Processing Script

Let's look at a complete example that demonstrates the filesystemobject to pathlib conversion in a practical scenario.

VBA Version

Sub ProcessReportFiles()
    Dim fso As Object
    Dim folder As Object
    Dim file As Object
    Dim processedCount As Integer
    
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set folder = fso.GetFolder("C:\Reports\Input")
    
    processedCount = 0
    
    For Each file In folder.Files
        If fso.GetExtensionName(file.Name) = "xlsx" Then
            ' Process the file (placeholder)
            Debug.Print "Processing: " & file.Name
            
            ' Move to processed folder
            Dim newPath As String
            newPath = "C:\Reports\Processed\" & file.Name
            
            If Not fso.FolderExists("C:\Reports\Processed") Then
                fso.CreateFolder "C:\Reports\Processed"
            End If
            
            fso.MoveFile file.Path, newPath
            processedCount = processedCount + 1
        End If
    Next file
    
    Debug.Print "Processed " & processedCount & " files"
End Sub

Python Version

from pathlib import Path
import shutil

def process_report_files():
    input_dir = Path("C:/Reports/Input")
    processed_dir = Path("C:/Reports/Processed")
    
    # Ensure directories exist
    processed_dir.mkdir(parents=True, exist_ok=True)
    
    processed_count = 0
    
    # Process all Excel files
    for file_path in input_dir.glob("*.xlsx"):
        print(f"Processing: {file_path.name}")
        
        # Process the file (placeholder)
        # your_processing_function(file_path)
        
        # Move to processed folder
        destination = processed_dir / file_path.name
        file_path.rename(destination)
        processed_count += 1
    
    print(f"Processed {processed_count} files")

# Run the function
if __name__ == "__main__":
    process_report_files()

The Python version is more concise, handles directory creation automatically, and uses more intuitive methods for file operations.

Conclusion

The transition from filesystemobject to pathlib represents more than just learning new syntax — it's about embracing a more powerful, cross-platform approach to file system operations. Python's pathlib offers several key advantages over VBA's FileSystemObject:

  • Object-oriented design that makes code more readable and maintainable
  • Cross-platform compatibility that eliminates Windows-specific limitations
  • Better error handling with specific exception types
  • Powerful pattern matching with glob and rglob methods
  • Intuitive path manipulation without complex string operations
  • Seamless integration with the broader