{"id":37070584,"url":"https://github.com/byteskeptical/sftpretty","last_synced_at":"2026-01-14T08:14:34.967Z","repository":{"id":40777579,"uuid":"373712237","full_name":"byteskeptical/sftpretty","owner":"byteskeptical","description":"Provides multi-threaded routines and high level protocol abstractions for a pretty quick \u0026 simple file transfer experience. Super duper close to a drop in replacement for pysftp.","archived":false,"fork":false,"pushed_at":"2025-12-21T08:58:07.000Z","size":5895,"stargazers_count":39,"open_issues_count":2,"forks_count":8,"subscribers_count":3,"default_branch":"root","last_synced_at":"2025-12-23T01:46:50.812Z","etag":null,"topics":["file-transfer","file-transfer-protocol","paramiko","pysftp","scp","sftp","sftp-client","sftp-download","sftp-server","sftp-upload","ssh"],"latest_commit_sha":null,"homepage":"https://docs.sftpretty.com/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/byteskeptical.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":"docs/contributing.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-06-04T03:45:31.000Z","updated_at":"2025-12-21T08:56:56.000Z","dependencies_parsed_at":"2024-06-21T13:45:45.104Z","dependency_job_id":"d1956bfc-f717-4644-89b4-7b4e32e08891","html_url":"https://github.com/byteskeptical/sftpretty","commit_stats":{"total_commits":152,"total_committers":3,"mean_commits":"50.666666666666664","dds":0.06578947368421051,"last_synced_commit":"ca413cdf0ff0db5242dc325c7bc5b9455bb33c1e"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/byteskeptical/sftpretty","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byteskeptical%2Fsftpretty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byteskeptical%2Fsftpretty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byteskeptical%2Fsftpretty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byteskeptical%2Fsftpretty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/byteskeptical","download_url":"https://codeload.github.com/byteskeptical/sftpretty/tar.gz/refs/heads/root","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/byteskeptical%2Fsftpretty/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413705,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["file-transfer","file-transfer-protocol","paramiko","pysftp","scp","sftp","sftp-client","sftp-download","sftp-server","sftp-upload","ssh"],"created_at":"2026-01-14T08:14:34.310Z","updated_at":"2026-01-14T08:14:34.942Z","avatar_url":"https://github.com/byteskeptical.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"sftpretty\n=========\n\nA pretty quick and simple interface to paramiko SFTP. Provides multi-threaded\nroutines with progress notifications for reliable, asynchronous transfers. A\nPython3 optimized fork of pysftp with additional features \u0026 improvements.\n\n* Built-in retry decorator\n* Channel cache\n* Hash function for integrity checking\n* Improved local \u0026 remote directory mapping\n* Improved logging mechanism\n* More tests\n* Multi-threaded directory transfers\n* OpenSSH config file support\n* Progress notifications\n* Support for ciphers, compression, digests, kex \u0026 key type options\n* Support for disabled algorithms\n* Support for ED25519 \u0026 ECDSA keys\n* Support for private key passwords\n* Thread-safe connection manager\n* Transfer Resumption\n\n\nExample\n-------\n.. code-block:: python\n\n    from sftpretty import CnOpts, Connection\n\n\n    # Basic\n\n    with Connection('hostname', username='me', password='secret') as sftp:\n        # Temporarily chdir to public/.\n        with sftp.cd('public'):\n            # Upload file to public/ on remote.\n            sftp.put('/my/local/filename')\n            # Download a remote file from public/.\n            sftp.get('remote_file')\n\n\n    with Connection('hostname', private_key='~/.ssh/id_ed25519',\n                    private_key_pass='secret') as sftp:\n        # Upload local directory to remote_directory.\n        sftp.put_d('/my/local', '/remote_directory')\n\n        # Recursively download a remote_directory and save it to /tmp locally.\n        sftp.get_r('remote_directory', '/tmp')\n\n\n    # Advanced\n\n    # Use password authentication\n    with Connection('hostname', username='me', password='secret') as sftp:\n        # Upload local directory to remote_directory. On occurance of any\n        # exception or child of, passed in the tuple, retry the operation.\n        # Between each attempt increment a pause equal to backoff * delay.\n        # Run a total of tries (six) times including the first attempt.\n        sftp.put_d('/my/local', '/remote_directory', backoff=2, delay=1,\n                   exceptions=(NoValidConnectionsError, socket.timeout,\n                               SSHException), tries=6)\n\n\n    # Use public key authentication\n    with Connection('hostname', private_key='~/.ssh/id_ed25519') as sftp:\n        # Resume the download of a bigfile and save it to /mnt locally.\n        sftp.get('bigfile', '/mnt', preserve_mtime=True, resume=True)\n\n\n    # Use public key authentication with optional private key password\n    with Connection('hostname', private_key='~/.ssh/id_ed25519',\n                    private_key_pass='secret') as sftp:\n        # Recursively download a remote_directory and save it to /tmp locally.\n        # Don't confirm files, useful in a scenario where the server removes\n        # the remote file immediately after download. Preserve remote mtime on\n        # local copy. Limit the thread pool connections to the server.\n        sftp.get_r('remote_directory', '/tmp', confirm=False,\n                   preserve_mtime=True, workers=6)\n\n\n    # Use OpenSSH config for public key authentication. Configuration\n    # connection values are prioritized when available. Credentials still need\n    # to be provided. There may be a significant delta between your ssh program\n    # and support for newer security option algorithms due to lagging support\n    # in paramiko.\n    cnopts = CnOpts(config='~/.ssh/config', knownhosts='server.pub')\n    with Connection('alias', cnopts=cnopts, private_key_pass='secret') as sftp:\n        # Rename existing file on remote server\n        sftp.rename('/remote/old_name', '/remote/new_name')\n\n\n    # Pass custom host key file for verification \n    cnopts = CnOpts(knownhosts='sftpserver.pub')\n    # Use connection options to set preferred encryption standards\n    cnopts.ciphers= ('aes256-ctr', 'aes128-ctr')\n    cnopts.digests = ('hmac-sha2-512', 'hmac-sha2-256')\n    cnopts.kex = ('ecdh-sha2-nistp521', 'ecdh-sha2-nistp384')\n    cnopts.key_types = ('ssh-ed25519', 'ecdsa-sha2-nistp521')\n    # Turn on verbose logging and set custom log file\n    cnopts.log = '/var/log/backups/daily.log'\n    cnopts.log_level = 'debug'\n    # Pass options object directly to connection object\n    with Connection('hostname', cnopts=cnopts, private_key='~/.ssh/id_backup',\n                    private_key_pass='secret') as sftp:\n        # Aggressively retry important operation\n        sftp.put_r('/local_backup', '/remote_backup', backoff=2, delay=1,\n                   exceptions=socket.timeout, preserve_mtime=True, tries=11)\n\n\n+-------------------+--------------------------+\n|                    API Diff                  |\n+-------------------+--------------------------+\n|      pysftp       |        sftpretty         |\n+===================+==========================+\n|        cwd        |          cd [#]_         |\n+-------------------+--------------------------+\n|     makedirs      |         mkdir_p          |\n+-------------------+--------------------------+\n|     walktree      |  {local,remote}tree [#]_ |\n+-------------------+--------------------------+\n\n.. [#] cwd() is a synonym for chdir(), use cd it's shorter and does the same thing.\n.. [#] Connection.walktree \u0026 sftp.walktree with explicit naming.\n.. [*] [path_advance, path_retreat, reparent] no longer needed.\n\n\nAdditional Information\n----------------------\n* Documentation: https://docs.sftpretty.com\n* Download: https://pypi.python.org/pypi/sftpretty\n* License: BSD\n* Project: https://github.com/byteskeptical/sftpretty\n\nRequirements\n------------\nparamiko \u003e= 2.7.0\n\nSupports\n--------\nTested on Python 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteskeptical%2Fsftpretty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyteskeptical%2Fsftpretty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteskeptical%2Fsftpretty/lists"}