{"action":"created","issue":{"url":"https://api.github.com/repos/ronf/asyncssh/issues/11","labels_url":"https://api.github.com/repos/ronf/asyncssh/issues/11/labels{/name}","comments_url":"https://api.github.com/repos/ronf/asyncssh/issues/11/comments","events_url":"https://api.github.com/repos/ronf/asyncssh/issues/11/events","html_url":"https://github.com/ronf/asyncssh/issues/11","id":64892283,"number":11,"title":"Support basic SFTP operations","user":{"login":"nchammas","id":1039369,"avatar_url":"https://avatars.githubusercontent.com/u/1039369?v=3","gravatar_id":"","url":"https://api.github.com/users/nchammas","html_url":"https://github.com/nchammas","followers_url":"https://api.github.com/users/nchammas/followers","following_url":"https://api.github.com/users/nchammas/following{/other_user}","gists_url":"https://api.github.com/users/nchammas/gists{/gist_id}","starred_url":"https://api.github.com/users/nchammas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nchammas/subscriptions","organizations_url":"https://api.github.com/users/nchammas/orgs","repos_url":"https://api.github.com/users/nchammas/repos","events_url":"https://api.github.com/users/nchammas/events{/privacy}","received_events_url":"https://api.github.com/users/nchammas/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/ronf/asyncssh/labels/enhancement","name":"enhancement","color":"84b6eb"}],"state":"closed","locked":false,"assignee":{"login":"ronf","id":1901085,"avatar_url":"https://avatars.githubusercontent.com/u/1901085?v=3","gravatar_id":"","url":"https://api.github.com/users/ronf","html_url":"https://github.com/ronf","followers_url":"https://api.github.com/users/ronf/followers","following_url":"https://api.github.com/users/ronf/following{/other_user}","gists_url":"https://api.github.com/users/ronf/gists{/gist_id}","starred_url":"https://api.github.com/users/ronf/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ronf/subscriptions","organizations_url":"https://api.github.com/users/ronf/orgs","repos_url":"https://api.github.com/users/ronf/repos","events_url":"https://api.github.com/users/ronf/events{/privacy}","received_events_url":"https://api.github.com/users/ronf/received_events","type":"User","site_admin":false},"milestone":null,"comments":10,"created_at":"2015-03-28T03:12:53Z","updated_at":"2015-05-30T00:19:23Z","closed_at":"2015-05-23T04:41:20Z","body":"Following from [the discussion here](https://github.com/ronf/asyncssh/issues/10#issuecomment-86248707), this is a request for SFTP support in AsyncSSH.\r\n\r\nI don't have any concrete proposal at this time, but I thought it would be useful to create this issue for tracking purposes."},"comment":{"url":"https://api.github.com/repos/ronf/asyncssh/issues/comments/106966142","html_url":"https://github.com/ronf/asyncssh/issues/11#issuecomment-106966142","issue_url":"https://api.github.com/repos/ronf/asyncssh/issues/11","id":106966142,"user":{"login":"nchammas","id":1039369,"avatar_url":"https://avatars.githubusercontent.com/u/1039369?v=3","gravatar_id":"","url":"https://api.github.com/users/nchammas","html_url":"https://github.com/nchammas","followers_url":"https://api.github.com/users/nchammas/followers","following_url":"https://api.github.com/users/nchammas/following{/other_user}","gists_url":"https://api.github.com/users/nchammas/gists{/gist_id}","starred_url":"https://api.github.com/users/nchammas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nchammas/subscriptions","organizations_url":"https://api.github.com/users/nchammas/orgs","repos_url":"https://api.github.com/users/nchammas/repos","events_url":"https://api.github.com/users/nchammas/events{/privacy}","received_events_url":"https://api.github.com/users/nchammas/received_events","type":"User","site_admin":false},"created_at":"2015-05-30T00:19:23Z","updated_at":"2015-05-30T00:19:23Z","body":"I tried out some basic get and put operations, and they look good to me. The API is also simple, which is great.\r\n\r\nI even got some tests setup using [Hypothesis](http://hypothesis.readthedocs.org/) like I [mentioned](https://github.com/ronf/asyncssh/issues/11#issuecomment-97313843) I wanted to try.\r\n\r\nCheck it out!\r\n\r\n```python\r\nimport asyncio\r\nimport asyncssh\r\n\r\nfrom hypothesis import given\r\nimport hypothesis.strategies as st\r\n\r\nPATH_PREFIX = '/some/path'\r\n\r\n\r\n@asyncio.coroutine\r\ndef put_file(a, b):\r\n    with (yield from asyncssh.connect('localhost')) as conn:\r\n        with (yield from conn.start_sftp_client()) as sftp:\r\n            yield from sftp.put(localpaths=a, remotepath=b)\r\n\r\n\r\ndef run_put_file(a, b):\r\n    (asyncio\r\n        .get_event_loop()\r\n        .run_until_complete(put_file(a, b)))\r\n\r\n\r\n@asyncio.coroutine\r\ndef get_file(a, b):\r\n    with (yield from asyncssh.connect('localhost')) as conn:\r\n        with (yield from conn.start_sftp_client()) as sftp:\r\n            yield from sftp.get(remotepaths=a, localpath=b)\r\n\r\ndef run_get_file(a, b):\r\n    (asyncio\r\n        .get_event_loop()\r\n        .run_until_complete(get_file(a, b)))\r\n\r\n\r\n@given(st.binary(max_size=(2 * 1024)))\r\ndef test_put_preserves_file(f):\r\n    before_path = PATH_PREFIX + '/before'\r\n    after_path = PATH_PREFIX + '/after'\r\n\r\n    with open(before_path, 'wb') as f1:\r\n        f1.write(f)\r\n\r\n    run_put_file(before_path, after_path)\r\n\r\n    with open(before_path, 'rb') as f1, open(after_path, 'rb') as f2:\r\n        assert f1.read() == f2.read()\r\n\r\n\r\n@given(st.binary(max_size=(2 * 1024)))\r\ndef test_get_preserves_file(f):\r\n    before_path = PATH_PREFIX + '/before'\r\n    after_path = PATH_PREFIX + '/after'\r\n\r\n    with open(before_path, 'wb') as f1:\r\n        f1.write(f)\r\n\r\n    run_get_file(before_path, after_path)\r\n\r\n    with open(before_path, 'rb') as f1, open(after_path, 'rb') as f2:\r\n        assert f1.read() == f2.read()\r\n```\r\n\r\nI ran these tests using pytest as follows:\r\n\r\n```\r\npython3 -m pytest asyncssh-hypothesis-tests.py\r\n```\r\n\r\nWhat these tests do is generate random 2 KB files and then make sure that `put()`-ing or `get()`-ing them preserves the contents of the file. You can configure how many iterations it goes through; I think it defaults to a few hundred, which took a couple of minutes on my laptop.\r\n\r\nIt's pretty neat! I found no bugs this way, which isn't surprising given how simple the test is. But perhaps this example will give you ideas for some more nefarious tests. :)\r\n\r\nHypothesis comes with many [built-in data generators](http://hypothesis.readthedocs.org/en/latest/data.html) like the binary generator I used above, and you can [write your own generators too](http://hypothesis.readthedocs.org/en/latest/data.html#hypothesis.strategies.basic).\r\n\r\nAnyway, thumbs up from me on the basics of the SFTP client. :+1: "}}