{"action":"created","comment":{"url":"https://api.github.com/repos/aio-libs/aioredis/pulls/comments/170982239","pull_request_review_id":99755091,"id":170982239,"diff_hunk":"@@ -0,0 +1,136 @@\n+import sys\n+from collections import OrderedDict\n+\n+from aioredis.util import wait_convert\n+\n+PY_VER = sys.version_info\n+\n+if PY_VER < (3, 0):\n+    from itertools import izip as zip\n+\n+\n+def fields_to_dict(fields):\n+    \"\"\"Convert a flat list of key/values into an OrderedDict\"\"\"\n+    fields_iterator = iter(fields)\n+    return OrderedDict(zip(fields_iterator, fields_iterator))\n+\n+\n+def parse_messages(messages):\n+    \"\"\" Parse messages as returned by Redis into something useful\n+\n+    Messages returned by XRANGE arrive in the form:\n+\n+        [\n+            [message_id, [key1, value1, key2, value2, ...]],\n+            ...\n+        ]\n+\n+    Here we parse this into:\n+\n+        [\n+            [message_id, OrderedDict(\n+                (key1, value1),\n+                (key2, value2),\n+                ...\n+            )],\n+            ...\n+        ]\n+\n+    \"\"\"\n+    if messages is None:\n+        return []\n+    return [(mid, fields_to_dict(values)) for mid, values in messages]\n+\n+\n+def parse_messages_by_stream(messages_by_stream):\n+    \"\"\" Parse messages returned by stream\n+\n+    Messages returned by XREAD arrive in the form:\n+        [stream_name,\n+            [\n+                [message_id, [key1, value1, key2, value2, ...]],\n+                ...\n+            ],\n+            ...\n+        ]\n+\n+    Here we parse this into (with the help of the above parse_messages()\n+    function):\n+\n+        [\n+            [stream_name, message_id, OrderedDict(\n+                (key1, value1),\n+                (key2, value2),.\n+                ...\n+            )],\n+            ...\n+        ]\n+\n+    \"\"\"\n+    if messages_by_stream is None:\n+        return []\n+\n+    parsed = []\n+    for stream, messages in messages_by_stream:\n+        for message_id, fields in parse_messages(messages):\n+            parsed.append((stream, message_id, fields))\n+    return parsed\n+\n+\n+class StreamCommandsMixin:\n+    \"\"\"Stream commands mixin\n+\n+    Streams are under development in Redis and\n+    not currently released.\n+    \"\"\"\n+\n+    def xadd(self, stream, fields, message_id=None, max_len=None, exact_len=False):\n+        \"\"\" Add a message to the specified stream\n+        \"\"\"\n+        args = []\n+        if max_len is not None:\n+            if exact_len:\n+                args.extend((b'MAXLEN', max_len))\n+            else:\n+                args.extend((b'MAXLEN', b'~', max_len))\n+\n+        args.append(message_id or b'*')","path":"aioredis/commands/streams.py","position":97,"original_position":97,"commit_id":"51943be6b239cda0f7cec3203007a834dbdd74ae","original_commit_id":"51943be6b239cda0f7cec3203007a834dbdd74ae","user":{"login":"hoh","id":404665,"avatar_url":"https://avatars0.githubusercontent.com/u/404665?v=4","gravatar_id":"","url":"https://api.github.com/users/hoh","html_url":"https://github.com/hoh","followers_url":"https://api.github.com/users/hoh/followers","following_url":"https://api.github.com/users/hoh/following{/other_user}","gists_url":"https://api.github.com/users/hoh/gists{/gist_id}","starred_url":"https://api.github.com/users/hoh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hoh/subscriptions","organizations_url":"https://api.github.com/users/hoh/orgs","repos_url":"https://api.github.com/users/hoh/repos","events_url":"https://api.github.com/users/hoh/events{/privacy}","received_events_url":"https://api.github.com/users/hoh/received_events","type":"User","site_admin":false},"body":"Why not specifying `message_id=b'*'` in the function arguments ?","created_at":"2018-02-27T16:29:02Z","updated_at":"2018-02-27T16:29:02Z","html_url":"https://github.com/aio-libs/aioredis/pull/299#discussion_r170982239","pull_request_url":"https://api.github.com/repos/aio-libs/aioredis/pulls/299","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/aio-libs/aioredis/pulls/comments/170982239"},"html":{"href":"https://github.com/aio-libs/aioredis/pull/299#discussion_r170982239"},"pull_request":{"href":"https://api.github.com/repos/aio-libs/aioredis/pulls/299"}}},"pull_request":{"url":"https://api.github.com/repos/aio-libs/aioredis/pulls/299","id":145309300,"html_url":"https://github.com/aio-libs/aioredis/pull/299","diff_url":"https://github.com/aio-libs/aioredis/pull/299.diff","patch_url":"https://github.com/aio-libs/aioredis/pull/299.patch","issue_url":"https://api.github.com/repos/aio-libs/aioredis/issues/299","number":299,"state":"open","locked":false,"title":"Adding XADD/XRANGE/XREAD stream commands","user":{"login":"adamcharnock","id":138215,"avatar_url":"https://avatars1.githubusercontent.com/u/138215?v=4","gravatar_id":"","url":"https://api.github.com/users/adamcharnock","html_url":"https://github.com/adamcharnock","followers_url":"https://api.github.com/users/adamcharnock/followers","following_url":"https://api.github.com/users/adamcharnock/following{/other_user}","gists_url":"https://api.github.com/users/adamcharnock/gists{/gist_id}","starred_url":"https://api.github.com/users/adamcharnock/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adamcharnock/subscriptions","organizations_url":"https://api.github.com/users/adamcharnock/orgs","repos_url":"https://api.github.com/users/adamcharnock/repos","events_url":"https://api.github.com/users/adamcharnock/events{/privacy}","received_events_url":"https://api.github.com/users/adamcharnock/received_events","type":"User","site_admin":false},"body":"@antirez is [in the process](http://antirez.com/news/114) of adding streams to Redis. This PR adds initial support for the ``XADD``/``XRANGE``/``XREAD`` commands in aioredis.\r\n\r\n## Tests\r\n\r\nI've written tests and added the *streams* Redis branch to the travis config. In theory, the tests will only be run when the streams branch is available. ~~The logic is a bit hacky, but I figured it will only be until streams makes it into 4.0~~ (now improved as the hacky version didn't actually work).\r\n\r\n## Style\r\n\r\nI'm very open to feedback on my implementation style for the new commands. I've tried to stick to the aioredis style, but I think I will have probably wandered here or there. Happy to make any corrections or alterations.\r\n\r\n## Reason\r\n\r\nThis is my first time using aioredis. I'm working on [another project](https://github.com/adamcharnock/lightbus) in which I would like to experiment with Redis streams, plus I also need asyncio support. I therefore thought I'd contribute this here.\r\n\r\nAs I said, happy to make any changes etc.\r\n\r\n## Remaining work\r\n\r\n- [ ] Add support for the ``MAXLEN`` parameter in ``xadd()``\r\n- [ ] There is a question regarding style in the source for ``xread()``","created_at":"2017-10-07T21:17:31Z","updated_at":"2018-02-27T16:29:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"23199063530d5c9005c7710b0494b5155b37eff1","assignee":null,"assignees":[],"requested_reviewers":[{"login":"popravich","id":323442,"avatar_url":"https://avatars0.githubusercontent.com/u/323442?v=4","gravatar_id":"","url":"https://api.github.com/users/popravich","html_url":"https://github.com/popravich","followers_url":"https://api.github.com/users/popravich/followers","following_url":"https://api.github.com/users/popravich/following{/other_user}","gists_url":"https://api.github.com/users/popravich/gists{/gist_id}","starred_url":"https://api.github.com/users/popravich/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/popravich/subscriptions","organizations_url":"https://api.github.com/users/popravich/orgs","repos_url":"https://api.github.com/users/popravich/repos","events_url":"https://api.github.com/users/popravich/events{/privacy}","received_events_url":"https://api.github.com/users/popravich/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[{"id":102215491,"url":"https://api.github.com/repos/aio-libs/aioredis/labels/enhancement","name":"enhancement","color":"84b6eb","default":true}],"milestone":null,"commits_url":"https://api.github.com/repos/aio-libs/aioredis/pulls/299/commits","review_comments_url":"https://api.github.com/repos/aio-libs/aioredis/pulls/299/comments","review_comment_url":"https://api.github.com/repos/aio-libs/aioredis/pulls/comments{/number}","comments_url":"https://api.github.com/repos/aio-libs/aioredis/issues/299/comments","statuses_url":"https://api.github.com/repos/aio-libs/aioredis/statuses/51943be6b239cda0f7cec3203007a834dbdd74ae","head":{"label":"adamcharnock:streams","ref":"streams","sha":"51943be6b239cda0f7cec3203007a834dbdd74ae","user":{"login":"adamcharnock","id":138215,"avatar_url":"https://avatars1.githubusercontent.com/u/138215?v=4","gravatar_id":"","url":"https://api.github.com/users/adamcharnock","html_url":"https://github.com/adamcharnock","followers_url":"https://api.github.com/users/adamcharnock/followers","following_url":"https://api.github.com/users/adamcharnock/following{/other_user}","gists_url":"https://api.github.com/users/adamcharnock/gists{/gist_id}","starred_url":"https://api.github.com/users/adamcharnock/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adamcharnock/subscriptions","organizations_url":"https://api.github.com/users/adamcharnock/orgs","repos_url":"https://api.github.com/users/adamcharnock/repos","events_url":"https://api.github.com/users/adamcharnock/events{/privacy}","received_events_url":"https://api.github.com/users/adamcharnock/received_events","type":"User","site_admin":false},"repo":{"id":106130243,"name":"aioredis","full_name":"adamcharnock/aioredis","owner":{"login":"adamcharnock","id":138215,"avatar_url":"https://avatars1.githubusercontent.com/u/138215?v=4","gravatar_id":"","url":"https://api.github.com/users/adamcharnock","html_url":"https://github.com/adamcharnock","followers_url":"https://api.github.com/users/adamcharnock/followers","following_url":"https://api.github.com/users/adamcharnock/following{/other_user}","gists_url":"https://api.github.com/users/adamcharnock/gists{/gist_id}","starred_url":"https://api.github.com/users/adamcharnock/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/adamcharnock/subscriptions","organizations_url":"https://api.github.com/users/adamcharnock/orgs","repos_url":"https://api.github.com/users/adamcharnock/repos","events_url":"https://api.github.com/users/adamcharnock/events{/privacy}","received_events_url":"https://api.github.com/users/adamcharnock/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/adamcharnock/aioredis","description":"asyncio (PEP 3156) Redis support","fork":true,"url":"https://api.github.com/repos/adamcharnock/aioredis","forks_url":"https://api.github.com/repos/adamcharnock/aioredis/forks","keys_url":"https://api.github.com/repos/adamcharnock/aioredis/keys{/key_id}","collaborators_url":"https://api.github.com/repos/adamcharnock/aioredis/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/adamcharnock/aioredis/teams","hooks_url":"https://api.github.com/repos/adamcharnock/aioredis/hooks","issue_events_url":"https://api.github.com/repos/adamcharnock/aioredis/issues/events{/number}","events_url":"https://api.github.com/repos/adamcharnock/aioredis/events","assignees_url":"https://api.github.com/repos/adamcharnock/aioredis/assignees{/user}","branches_url":"https://api.github.com/repos/adamcharnock/aioredis/branches{/branch}","tags_url":"https://api.github.com/repos/adamcharnock/aioredis/tags","blobs_url":"https://api.github.com/repos/adamcharnock/aioredis/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/adamcharnock/aioredis/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/adamcharnock/aioredis/git/refs{/sha}","trees_url":"https://api.github.com/repos/adamcharnock/aioredis/git/trees{/sha}","statuses_url":"https://api.github.com/repos/adamcharnock/aioredis/statuses/{sha}","languages_url":"https://api.github.com/repos/adamcharnock/aioredis/languages","stargazers_url":"https://api.github.com/repos/adamcharnock/aioredis/stargazers","contributors_url":"https://api.github.com/repos/adamcharnock/aioredis/contributors","subscribers_url":"https://api.github.com/repos/adamcharnock/aioredis/subscribers","subscription_url":"https://api.github.com/repos/adamcharnock/aioredis/subscription","commits_url":"https://api.github.com/repos/adamcharnock/aioredis/commits{/sha}","git_commits_url":"https://api.github.com/repos/adamcharnock/aioredis/git/commits{/sha}","comments_url":"https://api.github.com/repos/adamcharnock/aioredis/comments{/number}","issue_comment_url":"https://api.github.com/repos/adamcharnock/aioredis/issues/comments{/number}","contents_url":"https://api.github.com/repos/adamcharnock/aioredis/contents/{+path}","compare_url":"https://api.github.com/repos/adamcharnock/aioredis/compare/{base}...{head}","merges_url":"https://api.github.com/repos/adamcharnock/aioredis/merges","archive_url":"https://api.github.com/repos/adamcharnock/aioredis/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/adamcharnock/aioredis/downloads","issues_url":"https://api.github.com/repos/adamcharnock/aioredis/issues{/number}","pulls_url":"https://api.github.com/repos/adamcharnock/aioredis/pulls{/number}","milestones_url":"https://api.github.com/repos/adamcharnock/aioredis/milestones{/number}","notifications_url":"https://api.github.com/repos/adamcharnock/aioredis/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/adamcharnock/aioredis/labels{/name}","releases_url":"https://api.github.com/repos/adamcharnock/aioredis/releases{/id}","deployments_url":"https://api.github.com/repos/adamcharnock/aioredis/deployments","created_at":"2017-10-07T21:00:41Z","updated_at":"2017-10-07T21:00:43Z","pushed_at":"2018-01-14T19:51:57Z","git_url":"git://github.com/adamcharnock/aioredis.git","ssh_url":"git@github.com:adamcharnock/aioredis.git","clone_url":"https://github.com/adamcharnock/aioredis.git","svn_url":"https://github.com/adamcharnock/aioredis","homepage":null,"size":1181,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"aio-libs:master","ref":"master","sha":"8bd157ef50df28bb4786c7882b9997f3525baa65","user":{"login":"aio-libs","id":7049303,"avatar_url":"https://avatars2.githubusercontent.com/u/7049303?v=4","gravatar_id":"","url":"https://api.github.com/users/aio-libs","html_url":"https://github.com/aio-libs","followers_url":"https://api.github.com/users/aio-libs/followers","following_url":"https://api.github.com/users/aio-libs/following{/other_user}","gists_url":"https://api.github.com/users/aio-libs/gists{/gist_id}","starred_url":"https://api.github.com/users/aio-libs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aio-libs/subscriptions","organizations_url":"https://api.github.com/users/aio-libs/orgs","repos_url":"https://api.github.com/users/aio-libs/repos","events_url":"https://api.github.com/users/aio-libs/events{/privacy}","received_events_url":"https://api.github.com/users/aio-libs/received_events","type":"Organization","site_admin":false},"repo":{"id":20163574,"name":"aioredis","full_name":"aio-libs/aioredis","owner":{"login":"aio-libs","id":7049303,"avatar_url":"https://avatars2.githubusercontent.com/u/7049303?v=4","gravatar_id":"","url":"https://api.github.com/users/aio-libs","html_url":"https://github.com/aio-libs","followers_url":"https://api.github.com/users/aio-libs/followers","following_url":"https://api.github.com/users/aio-libs/following{/other_user}","gists_url":"https://api.github.com/users/aio-libs/gists{/gist_id}","starred_url":"https://api.github.com/users/aio-libs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aio-libs/subscriptions","organizations_url":"https://api.github.com/users/aio-libs/orgs","repos_url":"https://api.github.com/users/aio-libs/repos","events_url":"https://api.github.com/users/aio-libs/events{/privacy}","received_events_url":"https://api.github.com/users/aio-libs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aio-libs/aioredis","description":"asyncio (PEP 3156) Redis support","fork":false,"url":"https://api.github.com/repos/aio-libs/aioredis","forks_url":"https://api.github.com/repos/aio-libs/aioredis/forks","keys_url":"https://api.github.com/repos/aio-libs/aioredis/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aio-libs/aioredis/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aio-libs/aioredis/teams","hooks_url":"https://api.github.com/repos/aio-libs/aioredis/hooks","issue_events_url":"https://api.github.com/repos/aio-libs/aioredis/issues/events{/number}","events_url":"https://api.github.com/repos/aio-libs/aioredis/events","assignees_url":"https://api.github.com/repos/aio-libs/aioredis/assignees{/user}","branches_url":"https://api.github.com/repos/aio-libs/aioredis/branches{/branch}","tags_url":"https://api.github.com/repos/aio-libs/aioredis/tags","blobs_url":"https://api.github.com/repos/aio-libs/aioredis/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aio-libs/aioredis/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aio-libs/aioredis/git/refs{/sha}","trees_url":"https://api.github.com/repos/aio-libs/aioredis/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aio-libs/aioredis/statuses/{sha}","languages_url":"https://api.github.com/repos/aio-libs/aioredis/languages","stargazers_url":"https://api.github.com/repos/aio-libs/aioredis/stargazers","contributors_url":"https://api.github.com/repos/aio-libs/aioredis/contributors","subscribers_url":"https://api.github.com/repos/aio-libs/aioredis/subscribers","subscription_url":"https://api.github.com/repos/aio-libs/aioredis/subscription","commits_url":"https://api.github.com/repos/aio-libs/aioredis/commits{/sha}","git_commits_url":"https://api.github.com/repos/aio-libs/aioredis/git/commits{/sha}","comments_url":"https://api.github.com/repos/aio-libs/aioredis/comments{/number}","issue_comment_url":"https://api.github.com/repos/aio-libs/aioredis/issues/comments{/number}","contents_url":"https://api.github.com/repos/aio-libs/aioredis/contents/{+path}","compare_url":"https://api.github.com/repos/aio-libs/aioredis/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aio-libs/aioredis/merges","archive_url":"https://api.github.com/repos/aio-libs/aioredis/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aio-libs/aioredis/downloads","issues_url":"https://api.github.com/repos/aio-libs/aioredis/issues{/number}","pulls_url":"https://api.github.com/repos/aio-libs/aioredis/pulls{/number}","milestones_url":"https://api.github.com/repos/aio-libs/aioredis/milestones{/number}","notifications_url":"https://api.github.com/repos/aio-libs/aioredis/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aio-libs/aioredis/labels{/name}","releases_url":"https://api.github.com/repos/aio-libs/aioredis/releases{/id}","deployments_url":"https://api.github.com/repos/aio-libs/aioredis/deployments","created_at":"2014-05-25T19:29:57Z","updated_at":"2018-02-27T10:05:53Z","pushed_at":"2018-02-27T10:41:23Z","git_url":"git://github.com/aio-libs/aioredis.git","ssh_url":"git@github.com:aio-libs/aioredis.git","clone_url":"https://github.com/aio-libs/aioredis.git","svn_url":"https://github.com/aio-libs/aioredis","homepage":null,"size":1229,"stargazers_count":520,"watchers_count":520,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":92,"mirror_url":null,"archived":false,"open_issues_count":27,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":92,"open_issues":27,"watchers":520,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/aio-libs/aioredis/pulls/299"},"html":{"href":"https://github.com/aio-libs/aioredis/pull/299"},"issue":{"href":"https://api.github.com/repos/aio-libs/aioredis/issues/299"},"comments":{"href":"https://api.github.com/repos/aio-libs/aioredis/issues/299/comments"},"review_comments":{"href":"https://api.github.com/repos/aio-libs/aioredis/pulls/299/comments"},"review_comment":{"href":"https://api.github.com/repos/aio-libs/aioredis/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/aio-libs/aioredis/pulls/299/commits"},"statuses":{"href":"https://api.github.com/repos/aio-libs/aioredis/statuses/51943be6b239cda0f7cec3203007a834dbdd74ae"}},"author_association":"NONE"}}
{"actor":{"display_login":"hoh"}}