pathlib.PurePath - Pure Path Manipulation
Class:
pathlib.PurePath
Type: Abstract base class for pure paths
Category: File System Operations
Python Version: 3.4+
Overview
pathlib.PurePath is the base class for path manipulation without filesystem I/O operations. It provides cross-platform path handling, parsing, and manipulation methods that work with path strings without requiring the actual files or directories to exist. Use PurePath when you need path manipulation without filesystem access.
📚 Basic Usage
Simple Example
from pathlib import PurePath, PurePosixPath, PureWindowsPath
# Creating PurePath objects (automatically selects platform-appropriate type)
path = PurePath("documents/projects/readme.txt")
print(path) # documents/projects/readme.txt (Unix) or documents\projects\readme.txt (Windows)
# Cross-platform path operations
config_path = PurePath("config") / "app.yaml"
backup_path = config_path.with_suffix(".backup")
print(f"Original: {config_path}") # config/app.yaml
print(f"Backup: {backup_path}") # config/app.backup
print(f"Parent: {config_path.parent}") # config
print(f"Name: {config_path.name}") # app.yaml
Core Methods/Functions
from pathlib import PurePath
# Path parsing and properties
p = PurePath("/home/user/documents/data.tar.gz")
print(f"Parts: {p.parts}") # ('/', 'home', 'user', 'documents', 'data.tar.gz')
print(f"Name: {p.name}") # data.tar.gz
print(f"Stem: {p.stem}") # data.tar
print(f"Suffix: {p.suffix}") # .gz
print(f"Suffixes: {p.suffixes}") # ['.tar', '.gz']
print(f"Parent: {p.parent}") # /home/user/documents
print(f"Anchor: {p.anchor}") # /
# Path manipulation
new_path = p.with_name("config.yaml") # /home/user/documents/config.yaml
stem_path = p.with_stem("backup") # /home/user/documents/backup.tar.gz
suffix_path = p.with_suffix(".json") # /home/user/documents/data.tar.json
Common Patterns
from pathlib import PurePath
# Pattern 1: Cross-platform path building
def build_asset_path(category: str, filename: str) -> PurePath:
"""Build asset path that works on all platforms."""
return PurePath("assets") / category / filename
# Pattern 2: Path transformation pipeline
def transform_log_path(log_path: PurePath) -> PurePath:
"""Transform log file path to archive path."""
return (log_path.parent / "archive" /
log_path.with_suffix(".gz").name)
# Pattern 3: Path validation and normalization
def normalize_config_path(path_str: str) -> PurePath:
"""Normalize configuration file path."""
path = PurePath(path_str)
if not path.suffix:
path = path.with_suffix(".yaml")
return path
# Usage examples
asset_path = build_asset_path("images", "logo.png") # assets/images/logo.png
archive_path = transform_log_path(PurePath("logs/app.log")) # logs/archive/app.log.gz
config_path = normalize_config_path("database") # database.yaml
🔧 PurePath API Reference
Class Methods
| Method | Description | Parameters | Return Type | Example |
|---|---|---|---|---|
PurePath(*args) | Constructor | *args: str | PurePath | PurePath | PurePath("dir", "file.txt") |
Instance Methods - Path Manipulation
| Method | Description | Parameters | Return Type | Example |
|---|---|---|---|---|
joinpath(*args) | Join path components | *args: str | PurePath | PurePath | p.joinpath("subdir", "file") |
with_name(name) | Replace filename | name: str | PurePath | p.with_name("new.txt") |
with_stem(stem) | Replace filename stem | stem: str | PurePath | p.with_stem("new") |
with_suffix(suffix) | Replace file suffix | suffix: str | PurePath | p.with_suffix(".bak") |
relative_to(other) | Get relative path | other: PurePath | PurePath | p.relative_to(base) |
is_relative_to(other) | Check if relative | other: PurePath | bool | p.is_relative_to(base) |
Instance Methods - Comparison and Matching
| Method | Description | Parameters | Return Type | Example |
|---|---|---|---|---|
match(pattern) | Match against pattern | pattern: str | bool | p.match("*.py") |
is_absolute() | Check if absolute path | None | bool | p.is_absolute() |
is_relative() | Check if relative path | None | bool | p.is_relative() |
Properties
| Property | Description | Type | Example |
|---|---|---|---|
name | Final path component | str | "file.txt" |
stem | Final component without suffix | str | "file" |
suffix | File extension | str | ".txt" |
suffixes | All file extensions | list[str] | [".tar", ".gz"] |
parent | Parent directory | PurePath | PurePath("parent") |
parents | Sequence of ancestors | Sequence[PurePath] | [PurePath("dir"), PurePath(".")] |
parts | Path components tuple | tuple[str, ...] | ("dir", "subdir", "file.txt") |
anchor | Root portion of path | str | "/" or "C:\\" |
Special Methods
| Method | Description | Parameters | Return Type | Example |
|---|---|---|---|---|
__str__() | String representation | None | str | str(p) |
__fspath__() | Filesystem path protocol | None | str | os.fspath(p) |
__truediv__(other) | Path joining with / | other: str | PurePath | PurePath | p / "subdir" |
__eq__(other) | Equality comparison | other: PurePath | bool | p1 == p2 |
__hash__() | Hash for sets/dicts | None | int | hash(p) |
🐛 Common Errors and Troubleshooting
Typical Error Messages
from pathlib import PurePath
# Error 1: TypeError - Invalid argument types
try:
path = PurePath(123) # Numbers not allowed
except TypeError as e:
print(f"Invalid type: {e}")
# Solution: Convert to string first: PurePath(str(123))
# Error 2: ValueError - Invalid path operations
try:
path = PurePath("file.txt")
relative = path.relative_to("different/path")
except ValueError as e:
print(f"Cannot make relative: {e}")
# Solution: Ensure paths have common base
# Error 3: ValueError - Empty name/suffix
try:
path = PurePath("directory/")
new_path = path.with_name("") # Empty name not allowed
except ValueError as e:
print(f"Invalid name: {e}")
# Solution: Use non-empty name
# Error 4: NotImplementedError - Abstract class instantiation
try:
from pathlib import PurePath
path = PurePath() # PurePath is abstract
except TypeError as e:
print(f"Cannot instantiate: {e}")
# Solution: Use PurePath with arguments or concrete subclasses
Debugging Tips
from pathlib import PurePath
def debug_pure_path(p: PurePath):
"""Debug pure path information."""
print(f"Path: {p}")
print(f"String: '{str(p)}'")
print(f"Parts: {p.parts}")
print(f"Is absolute: {p.is_absolute()}")
print(f"Name: '{p.name}'")
print(f"Stem: '{p.stem}'")
print(f"Suffix: '{p.suffix}'")
print(f"Suffixes: {p.suffixes}")
print(f"Parent: {p.parent}")
print(f"Parents: {list(p.parents)}")
print(f"Anchor: '{p.anchor}'")
# Usage
debug_pure_path(PurePath("/home/user/document.tar.gz"))
Error Handling Patterns
from pathlib import PurePath
def safe_path_operation(base_path: str, relative_path: str) -> PurePath:
"""Safely perform path operations with error handling."""
try:
base = PurePath(base_path)
relative = PurePath(relative_path)
# Validate inputs
if not base.is_absolute():
raise ValueError(f"Base path must be absolute: {base}")
if relative.is_absolute():
raise ValueError(f"Relative path must be relative: {relative}")
return base / relative
except (TypeError, ValueError) as e:
print(f"Path operation failed: {e}")
return PurePath()
def safe_relative_path(path: str, base: str) -> PurePath:
"""Safely get relative path with fallback."""
try:
p = PurePath(path)
b = PurePath(base)
return p.relative_to(b)
except ValueError:
# Fallback: return original path if can't make relative
return PurePath(path)
🎯 Primary Use Cases
1. Configuration Path Management
Use Case: Manage configuration file paths across different environments and platforms Why PurePath: No filesystem I/O required, cross-platform compatibility, path manipulation
from pathlib import PurePath
from typing import Dict, List
class ConfigPathManager:
"""Manage configuration file paths without filesystem dependencies."""
def __init__(self, app_name: str, base_config_dir: str = None):
self.app_name = app_name
self.base_config_dir = PurePath(base_config_dir or "~/.config")
def get_config_path(self, environment: str, config_type: str) -> PurePath:
"""Get configuration file path for environment and type."""
return (self.base_config_dir / self.app_name / environment /
f"{config_type}.yaml")
def get_all_config_paths(self, environments: List[str]) -> Dict[str, Dict[str, PurePath]]:
"""Get all configuration paths for multiple environments."""
config_types = ["database", "logging", "cache", "auth"]
paths = {}
for env in environments:
paths[env] = {}
for config_type in config_types:
paths[env][config_type] = self.get_config_path(env, config_type)
return paths
def get_backup_path(self, config_path: PurePath) -> PurePath:
"""Generate backup path for configuration file."""
return config_path.with_suffix(f"{config_path.suffix}.backup")
def get_template_path(self, config_type: str) -> PurePath:
"""Get path for configuration template."""
return (PurePath("templates") / self.app_name /
f"{config_type}.template.yaml")
# Usage
config_mgr = ConfigPathManager("myapp", "/etc")
# Get paths for different environments
environments = ["development", "staging", "production"]
all_paths = config_mgr.get_all_config_paths(environments)
for env, configs in all_paths.items():
print(f"\n{env.title()} Environment:")
for config_type, path in configs.items():
print(f" {config_type}: {path}")
print(f" backup: {config_mgr.get_backup_path(path)}")
# Example output:
# Development Environment:
# database: /etc/myapp/development/database.yaml
# backup: /etc/myapp/development/database.yaml.backup
2. Asset Pipeline Path Generator
Use Case: Generate asset paths for web applications or game development Why PurePath: Path manipulation without filesystem checks, cross-platform support
from pathlib import PurePath
from typing import Dict, List, Optional
from enum import Enum
class AssetType(Enum):
IMAGE = "images"
SCRIPT = "scripts"
STYLE = "styles"
AUDIO = "audio"
VIDEO = "video"
DATA = "data"
class AssetPathGenerator:
"""Generate asset paths for web applications or games."""
def __init__(self, base_asset_dir: str = "assets"):
self.base_dir = PurePath(base_asset_dir)
# Define asset structure
self.asset_structure = {
AssetType.IMAGE: {
"icons": ["svg", "png", "ico"],
"backgrounds": ["jpg", "png", "webp"],
"sprites": ["png", "svg"],
"ui": ["png", "svg"]
},
AssetType.SCRIPT: {
"components": ["js", "ts"],
"modules": ["js", "ts"],
"workers": ["js", "ts"]
},
AssetType.STYLE: {
"themes": ["css", "scss"],
"components": ["css", "scss"],
"layouts": ["css", "scss"]
},
AssetType.AUDIO: {
"music": ["mp3", "ogg", "wav"],
"effects": ["mp3", "ogg", "wav"]
}
}
def get_asset_path(self, asset_type: AssetType, category: str,
filename: str, variant: Optional[str] = None) -> PurePath:
"""Generate asset path for specific asset."""
path = self.base_dir / asset_type.value / category
if variant:
path = path / variant
return path / filename
def get_versioned_path(self, asset_path: PurePath, version: str) -> PurePath:
"""Add version to asset path."""
stem = asset_path.stem
suffix = asset_path.suffix
return asset_path.with_name(f"{stem}.v{version}{suffix}")
def get_compressed_path(self, asset_path: PurePath,
compression: str = "min") -> PurePath:
"""Generate compressed asset path."""
stem = asset_path.stem
suffix = asset_path.suffix
return asset_path.with_name(f"{stem}.{compression}{suffix}")
def get_responsive_image_paths(self, base_image_path: PurePath,
sizes: List[int]) -> Dict[str, PurePath]:
"""Generate responsive image paths for different sizes."""
paths = {}
for size in sizes:
size_key = f"{size}w"
stem = base_image_path.stem
suffix = base_image_path.suffix
size_filename = f"{stem}-{size}w{suffix}"
paths[size_key] = base_image_path.with_name(size_filename)
return paths
def get_theme_paths(self, theme_name: str) -> Dict[str, PurePath]:
"""Generate all paths for a theme."""
theme_base = self.base_dir / AssetType.STYLE.value / "themes" / theme_name
return {
"main": theme_base / f"{theme_name}.scss",
"variables": theme_base / "_variables.scss",
"mixins": theme_base / "_mixins.scss",
"components": theme_base / "_components.scss",
"compiled": theme_base / f"{theme_name}.css",
"minified": theme_base / f"{theme_name}.min.css"
}
def generate_build_manifest(self) -> Dict[str, List[str]]:
"""Generate manifest of all expected asset paths."""
manifest = {}
for asset_type, categories in self.asset_structure.items():
asset_paths = []
for category, extensions in categories.items():
for ext in extensions:
# Example files for each category
example_path = self.get_asset_path(
asset_type, category, f"example.{ext}"
)
asset_paths.append(str(example_path))
manifest[asset_type.value] = asset_paths
return manifest
# Usage
asset_gen = AssetPathGenerator("public/assets")
# Generate specific asset paths
logo_path = asset_gen.get_asset_path(AssetType.IMAGE, "icons", "logo.svg")
script_path = asset_gen.get_asset_path(AssetType.SCRIPT, "components", "header.js")
print(f"Logo: {logo_path}") # public/assets/images/icons/logo.svg
print(f"Script: {script_path}") # public/assets/scripts/components/header.js
# Generate versioned and compressed variants
logo_v2 = asset_gen.get_versioned_path(logo_path, "2.1.0")
script_min = asset_gen.get_compressed_path(script_path)
print(f"Versioned logo: {logo_v2}") # public/assets/images/icons/logo.v2.1.0.svg
print(f"Minified script: {script_min}") # public/assets/scripts/components/header.min.js
# Generate responsive image paths
hero_image = asset_gen.get_asset_path(AssetType.IMAGE, "backgrounds", "hero.jpg")
responsive_paths = asset_gen.get_responsive_image_paths(hero_image, [320, 768, 1024, 1920])
print("\nResponsive image paths:")
for size, path in responsive_paths.items():
print(f" {size}: {path}")
# Generate theme paths
theme_paths = asset_gen.get_theme_paths("dark")
print("\nDark theme paths:")
for component, path in theme_paths.items():
print(f" {component}: {path}")
3. URL Path Handler
Use Case: Handle URL paths and routing in web applications Why PurePath: String manipulation without filesystem dependency, cross-platform consistency
from pathlib import PurePath, PurePosixPath
from typing import Dict, List, Optional, Tuple
from urllib.parse import quote, unquote
class URLPathHandler:
"""Handle URL paths using pathlib for consistent manipulation."""
def __init__(self, base_url: str = ""):
# Always use PurePosixPath for URLs (forward slashes)
self.base_url = base_url.rstrip("/")
def build_url_path(self, *segments) -> str:
"""Build URL path from segments."""
# Use PurePosixPath to ensure forward slashes
path = PurePosixPath("/")
for segment in segments:
if segment: # Skip empty segments
path = path / str(segment).strip("/")
return str(path)
def build_api_path(self, version: str, resource: str,
resource_id: Optional[str] = None,
action: Optional[str] = None) -> str:
"""Build RESTful API path."""
segments = ["api", f"v{version}", resource]
if resource_id:
segments.append(resource_id)
if action:
segments.append(action)
return self.build_url_path(*segments)
def parse_url_path(self, url_path: str) -> Dict[str, any]:
"""Parse URL path into components."""
path = PurePosixPath(url_path.strip("/"))
return {
"full_path": str(path),
"segments": list(path.parts) if path.parts != ("/",) else [],
"filename": path.name if path.suffix else None,
"extension": path.suffix,
"parent": str(path.parent) if path.parent != path else "/",
"is_absolute": path.is_absolute()
}
def extract_route_params(self, url_pattern: str,
actual_url: str) -> Dict[str, str]:
"""Extract parameters from URL based on pattern."""
pattern_path = PurePosixPath(url_pattern.strip("/"))
actual_path = PurePosixPath(actual_url.strip("/"))
pattern_parts = pattern_path.parts
actual_parts = actual_path.parts
if len(pattern_parts) != len(actual_parts):
return {}
params = {}
for pattern_part, actual_part in zip(pattern_parts, actual_parts):
if pattern_part.startswith(":"):
# Route parameter
param_name = pattern_part[1:]
params[param_name] = unquote(actual_part)
elif pattern_part != actual_part:
# Parts don't match
return {}
return params
def build_query_url(self, base_path: str,
params: Dict[str, any]) -> str:
"""Build URL with query parameters."""
url = self.base_url + base_path
if params:
query_parts = []
for key, value in params.items():
if isinstance(value, list):
for item in value:
query_parts.append(f"{quote(str(key))}={quote(str(item))}")
else:
query_parts.append(f"{quote(str(key))}={quote(str(value))}")
if query_parts:
url += "?" + "&".join(query_parts)
return url
def normalize_path(self, path: str) -> str:
"""Normalize URL path (remove double slashes, etc.)."""
# Handle special case of root path
if path in ("", "/"):
return "/"
# Use PurePosixPath for normalization
normalized = PurePosixPath("/" + path.strip("/"))
return str(normalized)
def get_breadcrumb_paths(self, url_path: str) -> List[Tuple[str, str]]:
"""Generate breadcrumb paths from URL."""
path = PurePosixPath(url_path.strip("/"))
breadcrumbs = []
current_path = PurePosixPath("/")
breadcrumbs.append(("Home", str(current_path)))
for part in path.parts:
current_path = current_path / part
# Convert path segment to display name
display_name = part.replace("-", " ").replace("_", " ").title()
breadcrumbs.append((display_name, str(current_path)))
return breadcrumbs
# Usage
url_handler = URLPathHandler("https://api.example.com")
# Build API paths
user_list = url_handler.build_api_path("1", "users")
user_detail = url_handler.build_api_path("1", "users", "123")
user_posts = url_handler.build_api_path("1", "users", "123", "posts")
print(f"Users API: {user_list}") # /api/v1/users
print(f"User detail: {user_detail}") # /api/v1/users/123
print(f"User posts: {user_posts}") # /api/v1/users/123/posts
# Parse URL paths
parsed = url_handler.parse_url_path("/products/electronics/laptops/")
print(f"Parsed path: {parsed}")
# Extract route parameters
pattern = "/api/v1/users/:user_id/posts/:post_id"
actual = "/api/v1/users/123/posts/456"
params = url_handler.extract_route_params(pattern, actual)
print(f"Route params: {params}") # {'user_id': '123', 'post_id': '456'}
# Build query URLs
search_url = url_handler.build_query_url("/search", {
"q": "python programming",
"category": ["books", "videos"],
"sort": "relevance"
})
print(f"Search URL: {search_url}")
# Generate breadcrumbs
breadcrumbs = url_handler.get_breadcrumb_paths("/products/electronics/laptops")
print("Breadcrumbs:")
for name, path in breadcrumbs:
print(f" {name}: {path}")
4. Template Path Manager
Use Case: Manage template file paths for code generators and build systems Why PurePath: Path manipulation without filesystem dependency, template organization
from pathlib import PurePath
from typing import Dict, List, Optional, Set
from enum import Enum
class TemplateType(Enum):
HTML = "html"
CSS = "css"
JAVASCRIPT = "js"
PYTHON = "py"
YAML = "yaml"
JSON = "json"
DOCKERFILE = "dockerfile"
MAKEFILE = "makefile"
class TemplatePathManager:
"""Manage template file paths for code generation and build systems."""
def __init__(self, templates_base_dir: str = "templates"):
self.base_dir = PurePath(templates_base_dir)
# Define template categories and their structures
self.template_structure = {
"web": {
"frameworks": ["react", "vue", "angular"],
"components": ["header", "footer", "navigation", "form"],
"layouts": ["base", "admin", "public"]
},
"backend": {
"apis": ["rest", "graphql", "grpc"],
"databases": ["mysql", "postgresql", "mongodb"],
"deployment": ["docker", "kubernetes", "terraform"]
},
"mobile": {
"platforms": ["ios", "android", "flutter"],
"components": ["navigation", "forms", "lists"]
},
"documentation": {
"types": ["api", "user-guide", "readme"],
"formats": ["markdown", "restructured-text", "html"]
}
}
def get_template_path(self, category: str, subcategory: str,
template_name: str,
template_type: TemplateType) -> PurePath:
"""Get path for a specific template."""
filename = f"{template_name}.{template_type.value}"
return self.base_dir / category / subcategory / filename
def get_component_template_paths(self, framework: str,
component_name: str) -> Dict[str, PurePath]:
"""Get all template paths for a component."""
base_path = self.base_dir / "web" / "frameworks" / framework / "components"
paths = {}
# Common component file types
file_types = {
"component": TemplateType.JAVASCRIPT,
"template": TemplateType.HTML,
"styles": TemplateType.CSS,
"test": TemplateType.JAVASCRIPT,
"story": TemplateType.JAVASCRIPT # Storybook stories
}
for file_type, template_type in file_types.items():
if file_type == "component":
filename = f"{component_name}.component.{template_type.value}"
elif file_type == "template":
filename = f"{component_name}.template.{template_type.value}"
elif file_type == "styles":
filename = f"{component_name}.styles.{template_type.value}"
elif file_type == "test":
filename = f"{component_name}.test.{template_type.value}"
elif file_type == "story":
filename = f"{component_name}.stories.{template_type.value}"
paths[file_type] = base_path / filename
return paths
def get_project_template_paths(self, project_type: str,
project_name: str) -> Dict[str, PurePath]:
"""Get all template paths for a complete project."""
project_base = self.base_dir / "projects" / project_type
# Common project files
common_templates = {
"readme": project_base / "README.md.template",
"gitignore": project_base / ".gitignore.template",
"license": project_base / "LICENSE.template",
"contributing": project_base / "CONTRIBUTING.md.template"
}
# Project-specific templates
specific_templates = {}
if project_type == "python":
specific_templates.update({
"setup": project_base / "setup.py.template",
"pyproject": project_base / "pyproject.toml.template",
"requirements": project_base / "requirements.txt.template",
"main": project_base / "main.py.template",
"test": project_base / "test_main.py.template"
})
elif project_type == "web":
specific_templates.update({
"package": project_base / "package.json.template",
"index": project_base / "index.html.template",
"webpack": project_base / "webpack.config.js.template",
"babel": project_base / ".babelrc.template"
})
elif project_type == "docker":
specific_templates.update({
"dockerfile": project_base / "Dockerfile.template",
"compose": project_base / "docker-compose.yml.template",
"dockerignore": project_base / ".dockerignore.template"
})
return {**common_templates, **specific_templates}
def get_config_template_paths(self, environment: str) -> Dict[str, PurePath]:
"""Get configuration template paths for different environments."""
config_base = self.base_dir / "config" / environment
return {
"app": config_base / "app.yaml.template",
"database": config_base / "database.yaml.template",
"logging": config_base / "logging.yaml.template",
"security": config_base / "security.yaml.template",
"cache": config_base / "cache.yaml.template"
}
def get_deployment_template_paths(self, platform: str,
app_name: str) -> Dict[str, PurePath]:
"""Get deployment template paths for different platforms."""
deployment_base = self.base_dir / "deployment" / platform
if platform == "kubernetes":
return {
"deployment": deployment_base / f"{app_name}-deployment.yaml.template",
"service": deployment_base / f"{app_name}-service.yaml.template",
"ingress": deployment_base / f"{app_name}-ingress.yaml.template",
"configmap": deployment_base / f"{app_name}-configmap.yaml.template",
"secret": deployment_base / f"{app_name}-secret.yaml.template"
}
elif platform == "docker":
return {
"dockerfile": deployment_base / "Dockerfile.template",
"compose": deployment_base / "docker-compose.yml.template",
"env": deployment_base / ".env.template"
}
elif platform == "terraform":
return {
"main": deployment_base / "main.tf.template",
"variables": deployment_base / "variables.tf.template",
"outputs": deployment_base / "outputs.tf.template",
"providers": deployment_base / "providers.tf.template"
}
return {}
def validate_template_structure(self) -> Dict[str, List[str]]:
"""Validate that all expected template paths are properly structured."""
missing_templates = {}
# Check each category and subcategory
for category, subcategories in self.template_structure.items():
category_missing = []
for subcategory, items in subcategories.items():
for item in items:
# Check if common template types exist
for template_type in [TemplateType.HTML, TemplateType.CSS, TemplateType.JAVASCRIPT]:
template_path = self.get_template_path(
category, subcategory, item, template_type
)
# In real usage, you'd check if file exists
# For PurePath, we just collect the expected paths
category_missing.append(str(template_path))
if category_missing:
missing_templates[category] = category_missing
return missing_templates
def get_template_inheritance_chain(self, template_path: PurePath) -> List[PurePath]:
"""Get template inheritance chain (base templates that this template extends)."""
# Example inheritance: specific -> framework -> base
inheritance_chain = [template_path]
# Add framework-level template
if "frameworks" in template_path.parts:
framework_template = (self.base_dir / "web" / "base" /
template_path.name)
inheritance_chain.append(framework_template)
# Add global base template
base_template = self.base_dir / "base" / template_path.name
inheritance_chain.append(base_template)
return inheritance_chain
# Usage
template_mgr = TemplatePathManager("project-templates")
# Get component template paths
react_button_paths = template_mgr.get_component_template_paths("react", "Button")
print("React Button Component Templates:")
for file_type, path in react_button_paths.items():
print(f" {file_type}: {path}")
# Get project template paths
python_project_paths = template_mgr.get_project_template_paths("python", "my-app")
print("\nPython Project Templates:")
for template_type, path in python_project_paths.items():
print(f" {template_type}: {path}")
# Get deployment templates
k8s_paths = template_mgr.get_deployment_template_paths("kubernetes", "web-api")
print("\nKubernetes Deployment Templates:")
for resource_type, path in k8s_paths.items():
print(f" {resource_type}: {path}")
# Get configuration templates
prod_config_paths = template_mgr.get_config_template_paths("production")
print("\nProduction Config Templates:")
for config_type, path in prod_config_paths.items():
print(f" {config_type}: {path}")
# Get template inheritance chain
button_template = template_mgr.get_template_path("web", "components", "button", TemplateType.HTML)
inheritance = template_mgr.get_template_inheritance_chain(button_template)
print(f"\nTemplate Inheritance Chain for {button_template.name}:")
for i, template in enumerate(inheritance):
print(f" {i + 1}. {template}")
Performance Considerations
Time Complexity Summary
| Operation | Time Complexity | Notes |
|---|---|---|
| Path creation | O(1) | Object instantiation |
Path joining (/) | O(k) | Where k = number of components |
| String conversion | O(1) | Cached string representation |
| Property access | O(1) | Computed on first access, then cached |
| Path comparison | O(n) | Where n = path length |
| Pattern matching | O(n*m) | Where n = path length, m = pattern length |
Basic Benchmarking
import timeit
from pathlib import PurePath
def benchmark_pure_path_operations():
"""Benchmark PurePath operations vs string operations."""
# Test path creation
purepath_creation = timeit.timeit(
lambda: PurePath("home/user/documents/file.txt"),
number=100000
)
string_creation = timeit.timeit(
lambda: "home/user/documents/file.txt",
number=100000
)
print(f"Path creation - PurePath: {purepath_creation:.4f}s, String: {string_creation:.4f}s")
# Test path joining
p = PurePath("home")
purepath_joining = timeit.timeit(
lambda: p / "user" / "documents" / "file.txt",
number=100000
)
string_joining = timeit.timeit(
lambda: "/".join(["home", "user", "documents", "file.txt"]),
number=100000
)
print(f"Path joining - PurePath: {purepath_joining:.4f}s, String: {string_joining:.4f}s")
# Test path parsing
path = PurePath("home/user/documents/data.tar.gz")
purepath_parsing = timeit.timeit(
lambda: (path.parent, path.name, path.suffix),
number=100000
)
path_str = "home/user/documents/data.tar.gz"
string_parsing = timeit.timeit(
lambda: (path_str.rsplit("/", 1)[0], path_str.split("/")[-1],
path_str.split(".")[-1] if "." in path_str else ""),
number=100000
)
print(f"Path parsing - PurePath: {purepath_parsing:.4f}s, String: {string_parsing:.4f}s")
# benchmark_pure_path_operations()
Memory Usage Tips
- PurePath objects are immutable - operations create new instances
- String representation is cached after first access
- Use same PurePath instances when possible to leverage caching
- Path properties are computed lazily and cached
from pathlib import PurePath
# Memory-efficient path handling
class PathCache:
"""Cache frequently used PurePath objects."""
def __init__(self):
self._cache = {}
def get_path(self, path_str: str) -> PurePath:
"""Get cached PurePath object."""
if path_str not in self._cache:
self._cache[path_str] = PurePath(path_str)
return self._cache[path_str]
# Usage
cache = PathCache()
path1 = cache.get_path("assets/images/logo.png") # Creates new PurePath
path2 = cache.get_path("assets/images/logo.png") # Returns cached PurePath
assert path1 is path2 # Same object
🎯 When to Use pathlib.PurePath
✅ Ideal Use Cases
- Path manipulation without filesystem I/O requirements
- Cross-platform path handling in configuration management
- URL path processing and routing systems
- Template path generation for code generators
- Asset pipeline management for build systems
- Configuration file path organization across environments
- Path validation and normalization before filesystem operations
- API path building and parameter extraction
❌ When NOT to Use pathlib.PurePath
- Need actual filesystem operations (use
pathlib.Pathinstead) - Working with legacy string-based APIs that don't accept Path objects
- High-performance string manipulation where object overhead matters
- Simple one-off path operations where string methods suffice
Alternative Solutions
- pathlib.Path: For filesystem I/O operations
- os.path: Legacy string-based path operations
- urllib.parse: For URL parsing and manipulation
- String methods: For simple path string operations
- Custom path classes: For specialized path handling requirements
Additional Learning Resources
Official Python Resources
- pathlib — Object-oriented filesystem paths
- PEP 428 — The pathlib module
- os.path — Common pathname manipulations
Related Topics
os.pathmodule - Traditional path operationsurllib.parsemodule - URL parsing- File Path Handling Best Practices
Design Patterns
- Strategy Pattern: Different path handling strategies for different platforms
- Template Method Pattern: Path manipulation templates for different use cases
- Builder Pattern: Complex path construction with fluent interfaces