{"id":6963,"library":"aioftp","title":"aioftp","description":"aioftp is an asynchronous FTP client and server library for Python's asyncio framework, providing a simple yet extensible API for file transfer operations. It is currently at version 0.27.2 and is actively maintained, with updates released irregularly as needed.","status":"active","version":"0.27.2","language":"en","source_language":"en","source_url":"https://github.com/aio-libs/aioftp","tags":["ftp","asyncio","client","server","network","file transfer","async","protocol"],"install":[{"cmd":"pip install aioftp","lang":"bash","label":"Default Install"},{"cmd":"pip install aioftp[socks]","lang":"bash","label":"With SOCKS proxy support"}],"dependencies":[{"reason":"Requires Python 3.10 or newer.","package":"Python","optional":false},{"reason":"Optional dependency for SOCKS proxy support.","package":"siosocks","optional":true}],"imports":[{"symbol":"Client","correct":"from aioftp import Client"},{"symbol":"Server","correct":"from aioftp import Server"},{"note":"Used as an async context manager for simplified client operations.","symbol":"Client.context","correct":"from aioftp import Client"},{"note":"Crucial for correct LIST command parsing in multithreaded applications.","symbol":"setlocale","correct":"from aioftp import setlocale"}],"quickstart":{"code":"import asyncio\nimport aioftp\nimport os\n\nasync def download_mp3_files(host, port, user, password):\n    \"\"\"Connects to an FTP server, lists files, and downloads MP3s.\"\"\"\n    print(f\"Connecting to ftp://{user}@{host}:{port}...\")\n    try:\n        async with aioftp.Client.context(host, port, user, password) as client:\n            print(\"Connected and logged in.\")\n            async for path, info in client.list(recursive=True):\n                if info.get(\"type\") == \"file\" and path.suffix == \".mp3\":\n                    print(f\"Downloading {path.name}...\")\n                    await client.download(path, path.name)\n                    print(f\"Downloaded {path.name}.\")\n    except aioftp.StatusCodeError as e:\n        print(f\"FTP error: {e}\")\n    except ConnectionRefusedError:\n        print(f\"Connection refused by the FTP server at {host}:{port}.\")\n    except Exception as e:\n        print(f\"An unexpected error occurred: {e}\")\n\nasync def main():\n    FTP_HOST = os.environ.get('FTP_HOST', 'ftp.example.com') # Replace with a real FTP server for testing\n    FTP_PORT = int(os.environ.get('FTP_PORT', '21'))\n    FTP_USER = os.environ.get('FTP_USER', 'anonymous')\n    FTP_PASS = os.environ.get('FTP_PASS', 'anonymous@example.com')\n\n    # Example with a mock server if you don't have a real one\n    # For a real server, ensure FTP_HOST, FTP_PORT, FTP_USER, FTP_PASS are set correctly\n    # For testing, you might need a local FTP server or a public test server (be cautious)\n    print(\"Starting aioftp client example...\")\n    await download_mp3_files(FTP_HOST, FTP_PORT, FTP_USER, FTP_PASS)\n\nif __name__ == \"__main__\":\n    # To run this, you need an accessible FTP server. For anonymous access:\n    # export FTP_HOST='test.rebex.net' # or another public test server\n    # export FTP_USER='demo'\n    # export FTP_PASS='password'\n    # Then run 'python your_script.py'\n    asyncio.run(main())\n","lang":"python","description":"This quickstart demonstrates how to use `aioftp.Client` with its `context` manager to connect to an FTP server, list files recursively, and download specific files (e.g., MP3s). It gracefully handles connection and FTP status errors. Remember to replace placeholder credentials and host with actual FTP server details for real-world usage, ideally through environment variables."},"warnings":[{"fix":"Ensure the C locale is set for `datetime.strftime` using `aioftp.setlocale()` as a context manager, especially in multithreaded environments. Alternatively, update your FTP server to support `MLSx` commands or provide a custom `parse_list_line_custom` routine.","message":"When using `client.list()` in multithreaded applications or with servers that don't support `MLSD`, the fallback `LIST` command parsing can fail due to locale-dependent date formatting. This is especially prevalent on Windows.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Avoid `AsyncPathIO` if performance is critical for local file operations. Consider `aioftp.PathIO` if blocking is acceptable, or `aioftp.MemoryPathIO` for in-memory file systems, or custom non-blocking IO implementations if specific performance requirements exist.","message":"Using `aioftp.AsyncPathIO` for file system operations, which wraps blocking calls with `asyncio.BaseEventLoop.run_in_executor()`, can be significantly slower than direct blocking `aioftp.PathIO` or in-memory `aioftp.MemoryPathIO` for certain use cases.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Prefer `pathlib.Path` objects for all path-related arguments in `aioftp` methods to ensure consistent and robust behavior, e.g., `await client.download(Path('remote_file.txt'), Path('local_file.txt'))`.","message":"While string paths often work, `aioftp` methods, especially for download/upload, internally benefit from or explicitly expect `pathlib.Path` instances. Mixing raw strings with `pathlib.Path` or making assumptions can lead to unexpected behavior or errors.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"If an event loop is already running, avoid calling `asyncio.run()`. Instead, use `await` if within an `async` function, or use libraries like `nest_asyncio` to allow nested event loops for specific environments. If starting the main application, ensure `asyncio.run()` is called only once at the top level.","cause":"Attempting to run `asyncio.run()` or `loop.run_until_complete()` when an `asyncio` event loop is already active in the current thread, often seen in environments like Jupyter notebooks or when embedding async code.","error":"RuntimeError: This event loop is already running"},{"fix":"Verify the exact path and filename on the FTP server. Check the user's permissions for the requested operation and path. Use `client.list()` or `client.stat()` to inspect the remote file system before attempting operations like download or rename.","cause":"The specified file or directory path does not exist on the FTP server or the user lacks the necessary permissions to access it. This is a common FTP protocol response for non-existent resources.","error":"aioftp.StatusCodeError: 550 Requested action not taken: file not found. (550)"},{"fix":"Check for server-side timeouts or specific server error logs. Ensure your client operations are timely and adhere to FTP protocol expectations. For long-running operations, consider implementing retry logic or keeping the connection alive with periodic NOOP commands if the server supports it. Review network stability.","cause":"The FTP server unexpectedly closed the connection, often due to a timeout, an invalid operation, or network issues.","error":"ConnectionResetError: Connection lost"}]}