Python Script Scheduling Fixes - Code Examples
Ready-to-use Python code snippets to fix scripts that work in terminal but fail when scheduled via cron or automation tools.
Python Script Scheduling Fixes - Code Examples
When your Python script works in terminal but not when scheduled, these ready-to-use code snippets will help you diagnose and fix the most common issues.
Complete Script Template for Scheduled Execution #
🐍 Try it yourself
Environment Detection and Setup #
🐍 Try it yourself
File Path Resolution Utilities #
import os
from pathlib import Path
class PathResolver:
"""Utility class for resolving file paths in scheduled scripts"""
def __init__(self, script_file=__file__):
self.script_path = Path(script_file).resolve()
self.script_dir = self.script_path.parent
self.project_root = self.find_project_root()
def find_project_root(self):
"""Find project root by looking for common markers"""
current = self.script_dir
markers = ['.git', 'requirements.txt', 'setup.py', 'pyproject.toml']
while current != current.parent:
if any((current / marker).exists() for marker in markers):
return current
current = current.parent
return self.script_dir # Fallback to script directory
def resolve_path(self, relative_path):
"""Resolve relative path from script directory"""
return (self.script_dir / relative_path).resolve()
def resolve_from_root(self, relative_path):
"""Resolve relative path from project root"""
return (self.project_root / relative_path).resolve()
def ensure_directory(self, path):
"""Ensure directory exists, create if necessary"""
path = Path(path)
path.mkdir(parents=True, exist_ok=True)
return path
# Usage example
resolver = PathResolver()
config_file = resolver.resolve_path('config.json')
logs_dir = resolver.ensure_directory(resolver.resolve_path('logs'))
Configuration Management #
import json
import os
from pathlib import Path
class ConfigManager:
"""Manage configuration for scheduled scripts"""
def __init__(self, config_file='script_config.json'):
self.script_dir = Path(__file__).parent.absolute()
self.config_file = self.script_dir / config_file
self.config = self.load_config()
def load_config(self):
"""Load configuration with fallback defaults"""
default_config = {
'python_executable': sys.executable,
'working_directory': str(self.script_dir),
'log_level': 'INFO',
'log_file': 'script.log',
'environment': {
'PATH': '/usr/local/bin:/usr/bin:/bin',
'PYTHONPATH': str(self.script_dir),
'MPLBACKEND': 'Agg'
},
'email_notifications': {
'enabled': False,
'smtp_server': 'localhost',
'recipients': []
}
}
if self.config_file.exists():
try:
with open(self.config_file, 'r') as f:
user_config = json.load(f)
# Merge with defaults
default_config.update(user_config)
except Exception as e:
print(f"Error loading config: {e}, using defaults")
return default_config
def apply_environment(self):
"""Apply environment variables from configuration"""
env_vars = self.config.get('environment', {})
for key, value in env_vars.items():
os.environ[key] = str(value)
def get(self, key, default=None):
"""Get configuration value with dot notation support"""
keys = key.split('.')
value = self.config
for k in keys:
value = value.get(k, default)
if value is None:
break
return value
# Usage
config = ConfigManager()
config.apply_environment()
log_level = config.get('log_level', 'INFO')
Cron-Safe Script Runner #
#!/usr/bin/env python3
import subprocess
import sys
import os
import logging
from pathlib import Path
def run_script_with_full_environment(script_path, python_executable=None):
"""Run a Python script with full environment setup"""
if python_executable is None:
python_executable = sys.executable
# Build environment with common paths
env = os.environ.copy()
# Common system paths
system_paths = [
'/usr/local/bin',
'/usr/bin',
'/bin',
'/usr/local/sbin',
'/usr/sbin',
'/sbin'
]
# Add user paths if they exist
user_paths = [
os.path.expanduser('~/.local/bin'),
os.path.expanduser('~/bin')
]
# Python-specific paths
python_paths = [
os.path.dirname(python_executable),
os.path.join(os.path.dirname(python_executable), 'Scripts') # Windows
]
all_paths = system_paths + user_paths + python_paths
existing_paths = [p for p in all_paths if os.path.exists(p)]
# Set comprehensive PATH
env['PATH'] = ':'.join(existing_paths + [env.get('PATH', '')])
# Set working directory to script location
script_dir = os.path.dirname(os.path.abspath(script_path))
# Run the script
try:
result = subprocess.run(
[python_executable, script_path],
cwd=script_dir,
env=env,
capture_output=True,
text=True,
timeout=3600 # 1 hour timeout
)
print(f"Exit code: {result.returncode}")
if result.stdout:
print(f"STDOUT:\n{result.stdout}")
if result.stderr:
print(f"STDERR:\n{result.stderr}")
return result.returncode
except subprocess.TimeoutExpired:
print("Script timed out after 1 hour")
return 124
except Exception as e:
print(f"Error running script: {e}")
return 1
# Usage for cron
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python cron_runner.py <script_path>")
sys.exit(1)
script_path = sys.argv[1]
exit_code = run_script_with_full_environment(script_path)
sys.exit(exit_code)
Summary #
These code snippets provide robust solutions for the most common issues when Python scripts work in terminal but fail when scheduled. The ScheduledScriptBase
class offers a complete foundation for scheduled scripts, while the utility functions help with environment detection, path resolution, and configuration management.
Key features included:
- Automatic environment setup and PATH configuration
- Comprehensive logging with environment information
- Robust error handling and recovery
- Configuration management with sensible defaults
- Cron-safe script execution wrapper
Use these snippets as building blocks to make your Python scripts reliable in any execution environment.