{"action":"created","comment":{"url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/comments/33533441","id":33533441,"diff_hunk":"@@ -0,0 +1,417 @@\n+# Copyright 2015 RethinkDB, all rights reserved.\n+\n+import struct\n+\n+from twisted.python import log\n+from twisted.internet import reactor, defer\n+from twisted.internet.defer import inlineCallbacks, returnValue, Deferred\n+from twisted.internet.defer import DeferredQueue, CancelledError\n+from twisted.internet.protocol import ClientFactory, Protocol\n+from twisted.internet.endpoints import TCP4ClientEndpoint\n+from twisted.internet.error import TimeoutError\n+\n+from . import ql2_pb2 as p\n+from .net import decodeUTF, Query, Response, Cursor, maybe_profile, convert_pseudo\n+from .net import Connection as ConnectionBase\n+from .errors import *\n+\n+__all__ = ['Connection']\n+\n+pResponse = p.Response.ResponseType\n+pQuery = p.Query.QueryType\n+\n+def start_timeout(timeout, *defersToFail):\n+    # Timeout is None == Infinite wait\n+    if timeout is None:\n+        return\n+\n+    def raisesTimeout():\n+        for deferToFail in defersToFail:\n+            deferToFail.cancel()\n+\n+    return reactor.callLater(timeout, raisesTimeout)\n+\n+class DatabaseProtocol(Protocol):\n+    WAITING_FOR_HANDSHAKE = 0\n+    READY = 1\n+\n+    def __init__(self, factory):\n+        self.factory = factory\n+        self.state = DatabaseProtocol.WAITING_FOR_HANDSHAKE\n+        self._handlers = {\n+            DatabaseProtocol.WAITING_FOR_HANDSHAKE: self._handleHandshake,\n+            DatabaseProtocol.READY: self._handleResponse\n+        }\n+\n+        self.buf = str()\n+        self.buf_expected_length = 0\n+        self.buf_token = None\n+\n+        self.wait_for_handshake = Deferred()\n+\n+        self._open = True\n+\n+    def resetBuffer(self):\n+        self.buf = ''\n+        self.buf_expected_length = 0\n+\n+    def connectionMade(self):\n+        # Send immediately the handshake.\n+        self.transport.write(self.factory.handshake_payload)\n+        # Defer a timer which will callback when timed out and errback the\n+        # wait_for_handshake. Otherwise, it will be cancelled in\n+        # handleHandshake.\n+        self._timeout_defer = reactor.callLater(self.factory.timeout,\n+                self._handleHandshakeTimeout)\n+\n+    def connectionLost(self, reason):\n+        self._open = False\n+\n+    def _handleHandshakeTimeout(self):\n+        # If we are here, we failed to do the handshake before the timeout.\n+        # We close the connection and raise an RqlTimeoutError in the\n+        # wait_for_handshake deferred.\n+        self.transport.loseConnection()\n+        self.wait_for_handshake.errback(RqlTimeoutError())\n+\n+    def _handleHandshake(self, data):\n+        try:\n+            self.buf += data\n+            if self.buf[-1] == b'\\0':\n+                message = decodeUTF(self.buf[:-1]).split('\\n')[0]\n+                if message != 'SUCCESS':\n+                    # If there is some problem with the handshake, we errback\n+                    # our deferred.\n+                    self.wait_for_handshake.errback(RqlDriverError('Server'\n+                    'dropped connection with message'\n+                    '\"{msg}\"'.format(msg=message)))\n+                else:\n+                    # We cancel the scheduled timeout.\n+                    self._timeout_defer.cancel()\n+                    # We callback our wait_for_handshake.\n+                    self.wait_for_handshake.callback(None)\n+                    # We're now ready to work with real data.\n+                    self.state = DatabaseProtocol.READY\n+                    self.resetBuffer()\n+        except Exception as e:\n+            self.wait_for_handshake.errback(e)\n+\n+    def _handleResponse(self, data):\n+        # 1. Read the header, until we read the length of the awaited payload.\n+        if self.buf_expected_length == 0:\n+            if data >= 12:\n+                token, length = struct.unpack('<qL', data[:12])\n+                self.buf_token = token\n+                self.buf_expected_length = length\n+                data = data[12:]\n+            else:\n+                self.buf += data\n+                # We quit the function, it is impossible to have read the\n+                # entire payload at this point.\n+                return\n+\n+        # 2. Buffer the data, until the size of the data match the expected\n+        # length provided by the header.\n+        self.buf += data\n+        if len(self.buf) >= self.buf_expected_length:\n+            expected_len = self.buf_expected_length\n+            if len(data) > expected_len:\n+                self.buf = self.buf[:expected_len]\n+            self.factory.response_handler(self.buf_token, self.buf)\n+            self.buf_token = None\n+            self.resetBuffer()\n+            # If we have more than a response, we should rehandle them.\n+            if len(data) > expected_len:\n+                self._handleResponse(data[expected_len:])\n+\n+\n+    def dataReceived(self, data):\n+        try:\n+            self._handlers[self.state](data)\n+        except Exception as e:\n+            raise RqlDriverError('Driver failed to handle received data.'\n+            'Error: {exc}. Dropping the connection.'.format(exc=str(e)))\n+            self.transport.loseConnection()\n+\n+class DatabaseProtoFactory(ClientFactory):\n+\n+    protocol = DatabaseProtocol\n+\n+    def __init__(self, timeout, response_handler, handshake_payload):\n+        self.timeout = timeout\n+        self.handshake_payload = handshake_payload\n+        self.response_handler = response_handler\n+\n+    def startedConnecting(self, connector):\n+        pass\n+\n+    def buildProtocol(self, addr):\n+        p = DatabaseProtocol(self)\n+        return p\n+\n+    def clientConnectionLost(self, connector, reason):\n+        pass\n+\n+    def clientConnectionFailed(self, connector, reason):\n+        pass\n+\n+class CursorItems(DeferredQueue):\n+\n+    def __init__(self):\n+        super(CursorItems, self).__init__(self)\n+\n+    @inlineCallbacks\n+    def wait_for_new_item(self):\n+        \"\"\"\n+        Callback whenever a new item arrived.\n+        It use the behavior of put.\n+        Warning: It ignores QueueUnderflow / QueueOverflow mechanisms.\n+        \"\"\"\n+        d = Deferred(canceller=self._cancelGet)\n+        self.waiting.append(d)\n+        item = yield d\n+        self.pending.append(item)\n+        returnValue(item)\n+\n+    def cancel_getters(self, err):\n+        \"\"\"\n+        Cancel all waiters.\n+        \"\"\"\n+        for waiter in self.waiting[:]:\n+            waiter.errback(err)\n+            self.waiting.remove(waiter)\n+\n+    def __len__(self):\n+        return len(self.pending)\n+\n+    def __getitem__(self, key):\n+        return self.pending[key]\n+\n+class TwistedCursor(Cursor):\n+\n+    def __init__(self, *args, **kwargs):\n+        super(TwistedCursor, self).__init__(*args, **kwargs)\n+        self.items = CursorItems()\n+        self.new_response = Deferred()\n+\n+    def _extend_queue(self, data):\n+        for k in data:\n+            self.items.put(k)\n+\n+    def _extend(self, res):\n+        self.outstanding_requests -= 1\n+        self.threshold = len(res.data)\n+        if self.error is None:\n+            if res.type == pResponse.SUCCESS_PARTIAL:\n+                self._extend_queue(res.data)\n+            elif res.type == pResponse.SUCCESS_SEQUENCE:\n+                self._extend_queue(res.data)\n+                self.error = self._empty_error()\n+                self.items.cancel_getters(self.error)\n+            else:\n+                self.error = res.make_error(self.query)\n+            self._maybe_fetch_batch()\n+\n+        if self.outstanding_requests == 0 and self.error is not None:\n+            del self.conn._cursor_cache[res.token]\n+\n+        self.new_response.callback(None)\n+        self.new_response = Deferred()\n+\n+    def _empty_error(self):\n+        return RqlCursorEmpty(self.query.term)\n+\n+    @inlineCallbacks\n+    def fetch_next(self, wait=True):\n+        timeout = Cursor._wait_to_timeout(wait)\n+        start_timeout(timeout, self.new_response)\n+        while len(self.items) == 0 and self.error is None:\n+            self._maybe_fetch_batch()\n+            try:\n+                yield self.new_response\n+            except CancelledError:\n+                raise RqlTimeoutError()\n+\n+        returnValue(not self.is_empty() or self.has_error())\n+\n+    def has_error(self):\n+        return self.error and (not isinstance(self.error, RqlCursorEmpty))\n+\n+    def is_empty(self):\n+        return isinstance(self.error, RqlCursorEmpty) and len(self.items) == 0\n+\n+    def _get_next(self, timeout):\n+        if self.is_empty() or self.has_error():","path":"drivers/python/rethinkdb/net_twisted.py","position":244,"original_position":244,"commit_id":"975e7bf222fcfbee2d4f56e6e9311f4c2c3c5901","original_commit_id":"975e7bf222fcfbee2d4f56e6e9311f4c2c3c5901","user":{"login":"Tryneus","id":552910,"avatar_url":"https://avatars.githubusercontent.com/u/552910?v=3","gravatar_id":"","url":"https://api.github.com/users/Tryneus","html_url":"https://github.com/Tryneus","followers_url":"https://api.github.com/users/Tryneus/followers","following_url":"https://api.github.com/users/Tryneus/following{/other_user}","gists_url":"https://api.github.com/users/Tryneus/gists{/gist_id}","starred_url":"https://api.github.com/users/Tryneus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Tryneus/subscriptions","organizations_url":"https://api.github.com/users/Tryneus/orgs","repos_url":"https://api.github.com/users/Tryneus/repos","events_url":"https://api.github.com/users/Tryneus/events{/privacy}","received_events_url":"https://api.github.com/users/Tryneus/received_events","type":"User","site_admin":false},"body":"This shouldn't fail until all successful rows have been read out from the cursor - that is, when `len(self.items) == 0`.  Only once all the items have been read should we throw the error.","created_at":"2015-06-30T01:38:03Z","updated_at":"2015-06-30T01:38:03Z","html_url":"https://github.com/rethinkdb/rethinkdb/pull/4096#discussion_r33533441","pull_request_url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096","_links":{"self":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/comments/33533441"},"html":{"href":"https://github.com/rethinkdb/rethinkdb/pull/4096#discussion_r33533441"},"pull_request":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096"}}},"pull_request":{"url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096","id":33886231,"html_url":"https://github.com/rethinkdb/rethinkdb/pull/4096","diff_url":"https://github.com/rethinkdb/rethinkdb/pull/4096.diff","patch_url":"https://github.com/rethinkdb/rethinkdb/pull/4096.patch","issue_url":"https://api.github.com/repos/rethinkdb/rethinkdb/issues/4096","number":4096,"state":"open","locked":false,"title":"Twisted backend for Python driver (#3785)","user":{"login":"RaitoBezarius","id":314564,"avatar_url":"https://avatars.githubusercontent.com/u/314564?v=3","gravatar_id":"","url":"https://api.github.com/users/RaitoBezarius","html_url":"https://github.com/RaitoBezarius","followers_url":"https://api.github.com/users/RaitoBezarius/followers","following_url":"https://api.github.com/users/RaitoBezarius/following{/other_user}","gists_url":"https://api.github.com/users/RaitoBezarius/gists{/gist_id}","starred_url":"https://api.github.com/users/RaitoBezarius/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RaitoBezarius/subscriptions","organizations_url":"https://api.github.com/users/RaitoBezarius/orgs","repos_url":"https://api.github.com/users/RaitoBezarius/repos","events_url":"https://api.github.com/users/RaitoBezarius/events{/privacy}","received_events_url":"https://api.github.com/users/RaitoBezarius/received_events","type":"User","site_admin":false},"body":"As discussed in #3785, here is my PR for the Twisted backend.\r\nIt has been tested (on a small part). Tests have been added (https://github.com/RaitoBezarius/rethinkdb/commit/2a8bac555fe88c40537451e746e69634c9818821), but I can't test yet (don't have the environnement for).\r\n\r\nKeep the great work going, guys!","created_at":"2015-04-22T18:22:29Z","updated_at":"2015-06-30T01:38:03Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d960e8711f3c2d2d17ce8e00393e3a9cd6d3eebd","assignee":{"login":"Tryneus","id":552910,"avatar_url":"https://avatars.githubusercontent.com/u/552910?v=3","gravatar_id":"","url":"https://api.github.com/users/Tryneus","html_url":"https://github.com/Tryneus","followers_url":"https://api.github.com/users/Tryneus/followers","following_url":"https://api.github.com/users/Tryneus/following{/other_user}","gists_url":"https://api.github.com/users/Tryneus/gists{/gist_id}","starred_url":"https://api.github.com/users/Tryneus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Tryneus/subscriptions","organizations_url":"https://api.github.com/users/Tryneus/orgs","repos_url":"https://api.github.com/users/Tryneus/repos","events_url":"https://api.github.com/users/Tryneus/events{/privacy}","received_events_url":"https://api.github.com/users/Tryneus/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096/commits","review_comments_url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096/comments","review_comment_url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rethinkdb/rethinkdb/issues/4096/comments","statuses_url":"https://api.github.com/repos/rethinkdb/rethinkdb/statuses/975e7bf222fcfbee2d4f56e6e9311f4c2c3c5901","head":{"label":"RaitoBezarius:next","ref":"next","sha":"975e7bf222fcfbee2d4f56e6e9311f4c2c3c5901","user":{"login":"RaitoBezarius","id":314564,"avatar_url":"https://avatars.githubusercontent.com/u/314564?v=3","gravatar_id":"","url":"https://api.github.com/users/RaitoBezarius","html_url":"https://github.com/RaitoBezarius","followers_url":"https://api.github.com/users/RaitoBezarius/followers","following_url":"https://api.github.com/users/RaitoBezarius/following{/other_user}","gists_url":"https://api.github.com/users/RaitoBezarius/gists{/gist_id}","starred_url":"https://api.github.com/users/RaitoBezarius/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RaitoBezarius/subscriptions","organizations_url":"https://api.github.com/users/RaitoBezarius/orgs","repos_url":"https://api.github.com/users/RaitoBezarius/repos","events_url":"https://api.github.com/users/RaitoBezarius/events{/privacy}","received_events_url":"https://api.github.com/users/RaitoBezarius/received_events","type":"User","site_admin":false},"repo":{"id":34319659,"name":"rethinkdb","full_name":"RaitoBezarius/rethinkdb","owner":{"login":"RaitoBezarius","id":314564,"avatar_url":"https://avatars.githubusercontent.com/u/314564?v=3","gravatar_id":"","url":"https://api.github.com/users/RaitoBezarius","html_url":"https://github.com/RaitoBezarius","followers_url":"https://api.github.com/users/RaitoBezarius/followers","following_url":"https://api.github.com/users/RaitoBezarius/following{/other_user}","gists_url":"https://api.github.com/users/RaitoBezarius/gists{/gist_id}","starred_url":"https://api.github.com/users/RaitoBezarius/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RaitoBezarius/subscriptions","organizations_url":"https://api.github.com/users/RaitoBezarius/orgs","repos_url":"https://api.github.com/users/RaitoBezarius/repos","events_url":"https://api.github.com/users/RaitoBezarius/events{/privacy}","received_events_url":"https://api.github.com/users/RaitoBezarius/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/RaitoBezarius/rethinkdb","description":"An open-source distributed JSON document database with a pleasant and powerful query language.","fork":true,"url":"https://api.github.com/repos/RaitoBezarius/rethinkdb","forks_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/forks","keys_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/teams","hooks_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/hooks","issue_events_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/issues/events{/number}","events_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/events","assignees_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/assignees{/user}","branches_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/branches{/branch}","tags_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/tags","blobs_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/git/refs{/sha}","trees_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/statuses/{sha}","languages_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/languages","stargazers_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/stargazers","contributors_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/contributors","subscribers_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/subscribers","subscription_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/subscription","commits_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/commits{/sha}","git_commits_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/git/commits{/sha}","comments_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/comments{/number}","issue_comment_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/issues/comments{/number}","contents_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/contents/{+path}","compare_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/merges","archive_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/downloads","issues_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/issues{/number}","pulls_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/pulls{/number}","milestones_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/milestones{/number}","notifications_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/labels{/name}","releases_url":"https://api.github.com/repos/RaitoBezarius/rethinkdb/releases{/id}","created_at":"2015-04-21T10:12:30Z","updated_at":"2015-04-22T20:39:05Z","pushed_at":"2015-06-25T14:33:54Z","git_url":"git://github.com/RaitoBezarius/rethinkdb.git","ssh_url":"git@github.com:RaitoBezarius/rethinkdb.git","clone_url":"https://github.com/RaitoBezarius/rethinkdb.git","svn_url":"https://github.com/RaitoBezarius/rethinkdb","homepage":"http://www.rethinkdb.com","size":118544,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"next"}},"base":{"label":"rethinkdb:next","ref":"next","sha":"bbed1b3adf13a6a742d1de9034e51016240a3105","user":{"login":"rethinkdb","id":1229647,"avatar_url":"https://avatars.githubusercontent.com/u/1229647?v=3","gravatar_id":"","url":"https://api.github.com/users/rethinkdb","html_url":"https://github.com/rethinkdb","followers_url":"https://api.github.com/users/rethinkdb/followers","following_url":"https://api.github.com/users/rethinkdb/following{/other_user}","gists_url":"https://api.github.com/users/rethinkdb/gists{/gist_id}","starred_url":"https://api.github.com/users/rethinkdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rethinkdb/subscriptions","organizations_url":"https://api.github.com/users/rethinkdb/orgs","repos_url":"https://api.github.com/users/rethinkdb/repos","events_url":"https://api.github.com/users/rethinkdb/events{/privacy}","received_events_url":"https://api.github.com/users/rethinkdb/received_events","type":"Organization","site_admin":false},"repo":{"id":6452529,"name":"rethinkdb","full_name":"rethinkdb/rethinkdb","owner":{"login":"rethinkdb","id":1229647,"avatar_url":"https://avatars.githubusercontent.com/u/1229647?v=3","gravatar_id":"","url":"https://api.github.com/users/rethinkdb","html_url":"https://github.com/rethinkdb","followers_url":"https://api.github.com/users/rethinkdb/followers","following_url":"https://api.github.com/users/rethinkdb/following{/other_user}","gists_url":"https://api.github.com/users/rethinkdb/gists{/gist_id}","starred_url":"https://api.github.com/users/rethinkdb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rethinkdb/subscriptions","organizations_url":"https://api.github.com/users/rethinkdb/orgs","repos_url":"https://api.github.com/users/rethinkdb/repos","events_url":"https://api.github.com/users/rethinkdb/events{/privacy}","received_events_url":"https://api.github.com/users/rethinkdb/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rethinkdb/rethinkdb","description":"An open-source distributed JSON document database with a pleasant and powerful query language.","fork":false,"url":"https://api.github.com/repos/rethinkdb/rethinkdb","forks_url":"https://api.github.com/repos/rethinkdb/rethinkdb/forks","keys_url":"https://api.github.com/repos/rethinkdb/rethinkdb/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rethinkdb/rethinkdb/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rethinkdb/rethinkdb/teams","hooks_url":"https://api.github.com/repos/rethinkdb/rethinkdb/hooks","issue_events_url":"https://api.github.com/repos/rethinkdb/rethinkdb/issues/events{/number}","events_url":"https://api.github.com/repos/rethinkdb/rethinkdb/events","assignees_url":"https://api.github.com/repos/rethinkdb/rethinkdb/assignees{/user}","branches_url":"https://api.github.com/repos/rethinkdb/rethinkdb/branches{/branch}","tags_url":"https://api.github.com/repos/rethinkdb/rethinkdb/tags","blobs_url":"https://api.github.com/repos/rethinkdb/rethinkdb/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rethinkdb/rethinkdb/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rethinkdb/rethinkdb/git/refs{/sha}","trees_url":"https://api.github.com/repos/rethinkdb/rethinkdb/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rethinkdb/rethinkdb/statuses/{sha}","languages_url":"https://api.github.com/repos/rethinkdb/rethinkdb/languages","stargazers_url":"https://api.github.com/repos/rethinkdb/rethinkdb/stargazers","contributors_url":"https://api.github.com/repos/rethinkdb/rethinkdb/contributors","subscribers_url":"https://api.github.com/repos/rethinkdb/rethinkdb/subscribers","subscription_url":"https://api.github.com/repos/rethinkdb/rethinkdb/subscription","commits_url":"https://api.github.com/repos/rethinkdb/rethinkdb/commits{/sha}","git_commits_url":"https://api.github.com/repos/rethinkdb/rethinkdb/git/commits{/sha}","comments_url":"https://api.github.com/repos/rethinkdb/rethinkdb/comments{/number}","issue_comment_url":"https://api.github.com/repos/rethinkdb/rethinkdb/issues/comments{/number}","contents_url":"https://api.github.com/repos/rethinkdb/rethinkdb/contents/{+path}","compare_url":"https://api.github.com/repos/rethinkdb/rethinkdb/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rethinkdb/rethinkdb/merges","archive_url":"https://api.github.com/repos/rethinkdb/rethinkdb/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rethinkdb/rethinkdb/downloads","issues_url":"https://api.github.com/repos/rethinkdb/rethinkdb/issues{/number}","pulls_url":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls{/number}","milestones_url":"https://api.github.com/repos/rethinkdb/rethinkdb/milestones{/number}","notifications_url":"https://api.github.com/repos/rethinkdb/rethinkdb/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rethinkdb/rethinkdb/labels{/name}","releases_url":"https://api.github.com/repos/rethinkdb/rethinkdb/releases{/id}","created_at":"2012-10-30T05:37:47Z","updated_at":"2015-06-30T00:33:59Z","pushed_at":"2015-06-30T00:08:59Z","git_url":"git://github.com/rethinkdb/rethinkdb.git","ssh_url":"git@github.com:rethinkdb/rethinkdb.git","clone_url":"https://github.com/rethinkdb/rethinkdb.git","svn_url":"https://github.com/rethinkdb/rethinkdb","homepage":"http://www.rethinkdb.com","size":457339,"stargazers_count":9262,"watchers_count":9262,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":787,"mirror_url":null,"open_issues_count":1040,"forks":787,"open_issues":1040,"watchers":9262,"default_branch":"next"}},"_links":{"self":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096"},"html":{"href":"https://github.com/rethinkdb/rethinkdb/pull/4096"},"issue":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/issues/4096"},"comments":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/issues/4096/comments"},"review_comments":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096/comments"},"review_comment":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/pulls/4096/commits"},"statuses":{"href":"https://api.github.com/repos/rethinkdb/rethinkdb/statuses/975e7bf222fcfbee2d4f56e6e9311f4c2c3c5901"}}}}