CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyftpdlib

Very fast asynchronous FTP server library providing RFC-959 compliant FTP servers with advanced features including FTPS, IPv6, Unicode support, and flexible authentication systems

Pending
Overview
Eval results
Files

utilities.mddocs/

Logging & Utilities

Logging configuration, process management, and utility functions for debugging, deployment, and command-line usage of pyftpdlib. These utilities provide essential support functions for production FTP server deployments.

Capabilities

Logging System

Comprehensive logging configuration with colored output, multi-process support, and flexible formatting options.

# Module-level variables
logger: logging.Logger  # Default pyftpdlib logger instance
LEVEL: int = logging.INFO  # Default logging level
PREFIX: str = '[%(levelname)1.1s %(asctime)s]'  # Default log prefix
PREFIX_MPROC: str = '[%(levelname)1.1s %(asctime)s %(process)s]'  # Multi-process prefix
COLOURED: bool  # Terminal color support detection
TIME_FORMAT: str = "%Y-%m-%d %H:%M:%S"  # Timestamp format

class LogFormatter(logging.Formatter):
    """
    Custom log formatter with color support and robust encoding.
    Features terminal color detection and proper str/bytes handling.
    """
    
    def __init__(self, *args, **kwargs):
        """Initialize formatter with color detection."""
    
    def format(self, record):
        """
        Format log record with colors and timestamps.
        
        Parameters:
        - record: logging.LogRecord instance
        
        Returns:
        - Formatted log message string
        """

def config_logging(level=LEVEL, prefix=PREFIX, other_loggers=None):
    """
    Configure pyftpdlib logging system.
    
    Parameters:
    - level: logging level (logging.DEBUG, logging.INFO, etc.)
    - prefix: log message prefix format string
    - other_loggers: list of other logger names to configure
    """

def debug(s, inst=None):
    """
    Debug logging helper function.
    
    Parameters:
    - s: message to log
    - inst: optional instance for context information
    """

def is_logging_configured():
    """
    Check if logging has been configured.
    
    Returns:
    - True if logging is already configured
    """

Process Management

Multi-process server support with worker process forking and automatic restart capabilities.

def cpu_count():
    """
    Get number of CPU cores available.
    
    Returns:
    - Number of CPU cores as integer
    """

def fork_processes(number, max_restarts=100):
    """
    Fork multiple worker processes for multi-process server model.
    
    Parameters:
    - number: number of worker processes to fork
    - max_restarts: maximum automatic restarts per worker (0 = no limit)
    
    Returns:
    - Process ID in parent process, None in worker processes
    
    Note:
    - Available on POSIX systems only
    - Handles SIGCHLD for automatic worker restart
    - Distributes work among processes using SO_REUSEPORT where available
    """

def _reseed_random():
    """
    Internal function to reseed random number generator in child processes.
    Ensures each worker process has independent random state.
    """

Command Line Interface

Full-featured command-line interface for quick FTP server deployment with extensive configuration options.

def main(args=None):
    """
    Main entry point for command-line FTP server.
    Accessible via: python -m pyftpdlib
    
    Parameters:
    - args: command line arguments list (None = use sys.argv)
    
    Command-line options:
    -i, --interface ADDRESS     Bind to specific interface (default: all interfaces)
    -p, --port PORT            Port number (default: 2121)
    -w, --write                Enable write permissions for anonymous
    -d, --directory FOLDER     Directory to serve (default: current directory)
    -n, --nat-address ADDRESS  NAT address to use in PASV responses
    -r, --range FROM-TO        Passive port range (e.g., 8000-9000)
    -D, --debug                Enable debug logging
    -v, --version              Show version and exit
    -V, --verbose              Enable verbose logging
    -u, --username USER        Username for authentication (requires -P)
    -P, --password PASS        Password for authentication (requires -u)
    """

Usage Examples

Basic Logging Configuration

from pyftpdlib.log import config_logging, debug
import logging

# Configure basic logging
config_logging(level=logging.INFO)

# Configure debug logging with custom prefix
config_logging(
    level=logging.DEBUG,
    prefix='[%(asctime)s] %(name)s: %(levelname)s - '
)

# Configure logging for other components
config_logging(
    level=logging.WARNING,
    other_loggers=['paramiko', 'requests']
)

# Use debug helper
debug("Connection established", inst=handler_instance)

Multi-Process Server

from pyftpdlib.prefork import fork_processes, cpu_count
from pyftpdlib.servers import FTPServer

def start_worker():
    """Worker process main function."""
    # Setup server in worker process
    server = FTPServer(("0.0.0.0", 21), handler)
    server.serve_forever()

# Fork worker processes
num_workers = cpu_count()
print(f"Starting {num_workers} worker processes")

if fork_processes(num_workers) is None:
    # This is a worker process
    start_worker()
else:
    # This is the parent process
    print("All workers started")
    # Parent can exit or do other work

Command Line Usage

# Basic FTP server on default port 2121
python -m pyftpdlib

# FTP server with write access for anonymous users
python -m pyftpdlib --write

# Custom port and directory
python -m pyftpdlib -p 21 -d /var/ftp/pub

# Server with authentication
python -m pyftpdlib -u ftpuser -P secret123

# Advanced configuration
python -m pyftpdlib \
    --interface 0.0.0.0 \
    --port 21 \
    --directory /home/ftp \
    --nat-address 203.0.113.10 \
    --range 50000-50099 \
    --verbose

Custom Logging Handler

from pyftpdlib.log import LogFormatter
import logging
import sys

class CustomFTPHandler(FTPHandler):
    def __init__(self, conn, server, ioloop=None):
        super().__init__(conn, server, ioloop)
        
        # Setup custom logger for this session
        self.session_logger = logging.getLogger(f'ftp.{self.remote_ip}')
        handler = logging.StreamHandler(sys.stdout)
        handler.setFormatter(LogFormatter())
        self.session_logger.addHandler(handler)
        self.session_logger.setLevel(logging.INFO)
    
    def on_login(self, username):
        self.session_logger.info(f"User {username} logged in")
    
    def on_file_sent(self, file):
        self.session_logger.info(f"File sent: {file}")
    
    def on_logout(self, username):
        self.session_logger.info(f"User {username} logged out")

Production Logging Setup

import logging
import logging.handlers
from pyftpdlib.log import LogFormatter

def setup_production_logging():
    """Configure logging for production deployment."""
    
    # Main application logger
    logger = logging.getLogger('pyftpdlib')
    logger.setLevel(logging.INFO)
    
    # File handler with rotation
    file_handler = logging.handlers.RotatingFileHandler(
        '/var/log/pyftpdlib.log',
        maxBytes=10*1024*1024,  # 10MB
        backupCount=5
    )
    file_handler.setFormatter(LogFormatter())
    logger.addHandler(file_handler)
    
    # Syslog handler for system integration
    syslog_handler = logging.handlers.SysLogHandler(address='/dev/log')
    syslog_formatter = logging.Formatter(
        'pyftpdlib[%(process)d]: %(levelname)s - %(message)s'
    )
    syslog_handler.setFormatter(syslog_formatter)
    logger.addHandler(syslog_handler)
    
    # Console handler for interactive debugging
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(LogFormatter())
    console_handler.setLevel(logging.WARNING)
    logger.addHandler(console_handler)

setup_production_logging()

Process Management with Restart Logic

import signal
import sys
import time
from pyftpdlib.prefork import fork_processes

class ProcessManager:
    def __init__(self, worker_count=None):
        self.worker_count = worker_count or cpu_count()
        self.should_exit = False
        
    def signal_handler(self, signum, frame):
        """Handle shutdown signals."""
        print(f"Received signal {signum}, shutting down...")
        self.should_exit = True
        
    def start_master(self):
        """Start master process with worker management."""
        # Setup signal handlers
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        
        print(f"Starting master process with {self.worker_count} workers")
        
        # Fork workers with restart capability
        if fork_processes(self.worker_count, max_restarts=10) is None:
            # Worker process
            self.start_worker()
        else:
            # Master process
            self.monitor_workers()
    
    def start_worker(self):
        """Worker process main loop."""
        try:
            server = FTPServer(("0.0.0.0", 21), handler)
            server.serve_forever()
        except KeyboardInterrupt:
            pass
        except Exception as e:
            print(f"Worker error: {e}")
            sys.exit(1)
    
    def monitor_workers(self):
        """Master process monitoring loop."""
        while not self.should_exit:
            time.sleep(1)
        
        print("Master process exiting")

# Usage
manager = ProcessManager(worker_count=4)
manager.start_master()

Development vs Production Configuration

import os
from pyftpdlib.log import config_logging

def configure_for_environment():
    """Configure logging based on environment."""
    
    if os.getenv('ENVIRONMENT') == 'development':
        # Development: verbose console logging
        config_logging(
            level=logging.DEBUG,
            prefix='[%(asctime)s] %(name)s:%(lineno)d - %(levelname)s - '
        )
        print("Development logging enabled")
        
    elif os.getenv('ENVIRONMENT') == 'production':
        # Production: structured logging to files
        config_logging(
            level=logging.INFO,
            prefix='[%(asctime)s] %(process)d - %(levelname)s - '
        )
        print("Production logging enabled")
        
    else:
        # Default: standard logging
        config_logging(level=logging.INFO)

configure_for_environment()

Integration with systemd

#!/usr/bin/env python3
"""
Systemd-compatible FTP server script.
Save as /usr/local/bin/pyftpd and make executable.
"""

import sys
import logging
from pyftpdlib.log import config_logging
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer

def main():
    # Configure logging for systemd (no timestamps, systemd adds them)
    config_logging(
        level=logging.INFO,
        prefix='%(levelname)s - '
    )
    
    # Setup FTP server
    authorizer = DummyAuthorizer()
    authorizer.add_anonymous('/var/ftp/pub', perm='elr')
    
    handler = FTPHandler
    handler.authorizer = authorizer
    
    server = FTPServer(('0.0.0.0', 21), handler)
    
    try:
        logging.info("Starting FTP server")
        server.serve_forever()
    except KeyboardInterrupt:
        logging.info("Received SIGINT, shutting down")
    except Exception as e:
        logging.error(f"Server error: {e}")
        sys.exit(1)
    finally:
        server.close_all()
        logging.info("FTP server stopped")

if __name__ == '__main__':
    main()

Corresponding systemd service file (/etc/systemd/system/pyftpd.service):

[Unit]
Description=Python FTP Server
After=network.target

[Service]
Type=simple
User=ftp
Group=ftp
ExecStart=/usr/local/bin/pyftpd
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Environment Variables

pyftpdlib respects several environment variables for configuration:

  • PYTHONUNBUFFERED: Disable output buffering for real-time logging
  • PYFTPDLIB_DEBUG: Enable debug logging when set to any value
  • PYFTPDLIB_LOG_PREFIX: Override default log prefix format

Platform Support

  • Process Management: POSIX systems only (Linux, macOS, BSD)
  • Logging: All platforms with appropriate terminal color detection
  • Command Line Interface: All platforms with cross-platform path handling

Install with Tessl CLI

npx tessl i tessl/pypi-pyftpdlib

docs

authorizers.md

filesystems.md

handlers.md

index.md

ioloop.md

servers.md

utilities.md

tile.json