{"action":"created","comment":{"url":"https://api.github.com/repos/twisted/txaws/pulls/comments/94061113","pull_request_review_id":14590706,"id":94061113,"diff_hunk":"@@ -0,0 +1,102 @@\n+\"\"\"\n+An in-memory implementation of the S3 client interface as an aid\n+to unit testing.\n+\"\"\"\n+\n+__all__ = [\n+    \"MemoryS3\",\n+]\n+\n+from weakref import WeakKeyDictionary\n+\n+from twisted.internet.defer import succeed, fail\n+\n+from txaws.s3.model import Bucket, BucketListing\n+from txaws.s3.exception import S3Error\n+\n+def _rate_limited(f):\n+    def g(self, *a, **kw):\n+        if self._state.get_rate_limit_exceeded():\n+            return fail(S3Error(\"<slowdown/>\", 400))\n+        return f(self, *a, **kw)\n+    return g\n+\n+\n+class MemoryS3(object):\n+    \"\"\"\n+    ``MemoryS3`` is a factory for new S3 clients.\n+    \"\"\"\n+    def __init__(self):\n+        self._state = WeakKeyDictionary()\n+\n+    def get_state(self, client):\n+        return self._state.setdefault(client, _S3ClientState())\n+\n+    def client(self, *a, **kw):\n+        client = _MemoryS3Client(self, *a, **kw)\n+        return client, self.get_state(client)\n+\n+\n+class S3ClientState(object):\n+    \"\"\"\n+    ``_S3ClientState`` instances hold the ``_MemoryS3Client`` instance\n+    state that is specific to testing and does not exist on\n+    ``txaws.s3.S3Client`` instances.\n+    \"\"\"\n+    from time import time\n+\n+    rate_limited_exceeded = False\n+\n+    def __init__(self):\n+        self.buckets = {}\n+\n+    def set_rate_limit_exceeded(self):\n+        self.rate_limit_exceeded = True\n+\n+    def clear_rate_limit_exceeded(self):\n+        self.rate_limit_exceeded = False\n+\n+    def get_rate_limit_exceeded(self,):\n+        return self.rate_limit_exceeded\n+\n+\n+class _ControllerState(object):\n+    def __get__(self, oself, type):\n+        return oself._controller.get_state(oself)\n+\n+\n+class _MemoryS3Client(object):\n+    _state = ControllerState()","path":"txaws/testing/s3.py","position":69,"original_position":69,"commit_id":"5f87bb36a10bbe75ccbf76a0f4a63f9115c984d8","original_commit_id":"5f87bb36a10bbe75ccbf76a0f4a63f9115c984d8","user":{"login":"mithrandi","id":856430,"avatar_url":"https://avatars.githubusercontent.com/u/856430?v=3","gravatar_id":"","url":"https://api.github.com/users/mithrandi","html_url":"https://github.com/mithrandi","followers_url":"https://api.github.com/users/mithrandi/followers","following_url":"https://api.github.com/users/mithrandi/following{/other_user}","gists_url":"https://api.github.com/users/mithrandi/gists{/gist_id}","starred_url":"https://api.github.com/users/mithrandi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mithrandi/subscriptions","organizations_url":"https://api.github.com/users/mithrandi/orgs","repos_url":"https://api.github.com/users/mithrandi/repos","events_url":"https://api.github.com/users/mithrandi/events{/privacy}","received_events_url":"https://api.github.com/users/mithrandi/received_events","type":"User","site_admin":false},"body":"And this should presumably be `_ControllerState`.","created_at":"2016-12-28T18:11:46Z","updated_at":"2016-12-28T18:13:59Z","html_url":"https://github.com/twisted/txaws/pull/7#discussion_r94061113","pull_request_url":"https://api.github.com/repos/twisted/txaws/pulls/7","_links":{"self":{"href":"https://api.github.com/repos/twisted/txaws/pulls/comments/94061113"},"html":{"href":"https://github.com/twisted/txaws/pull/7#discussion_r94061113"},"pull_request":{"href":"https://api.github.com/repos/twisted/txaws/pulls/7"}}},"pull_request":{"url":"https://api.github.com/repos/twisted/txaws/pulls/7","id":98007843,"html_url":"https://github.com/twisted/txaws/pull/7","diff_url":"https://github.com/twisted/txaws/pull/7.diff","patch_url":"https://github.com/twisted/txaws/pull/7.patch","issue_url":"https://api.github.com/repos/twisted/txaws/issues/7","number":7,"state":"open","locked":false,"title":"Helper for writing tests for S3-using code","user":{"login":"exarkun","id":254565,"avatar_url":"https://avatars.githubusercontent.com/u/254565?v=3","gravatar_id":"","url":"https://api.github.com/users/exarkun","html_url":"https://github.com/exarkun","followers_url":"https://api.github.com/users/exarkun/followers","following_url":"https://api.github.com/users/exarkun/following{/other_user}","gists_url":"https://api.github.com/users/exarkun/gists{/gist_id}","starred_url":"https://api.github.com/users/exarkun/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/exarkun/subscriptions","organizations_url":"https://api.github.com/users/exarkun/orgs","repos_url":"https://api.github.com/users/exarkun/repos","events_url":"https://api.github.com/users/exarkun/events{/privacy}","received_events_url":"https://api.github.com/users/exarkun/received_events","type":"User","site_admin":false},"body":"This is definitely incomplete.  Submitted for feedback on the general approach and comments about what might be missing in terms of tests for this testing helper.","created_at":"2016-12-14T18:24:21Z","updated_at":"2016-12-28T18:13:59Z","closed_at":null,"merged_at":null,"merge_commit_sha":"9635ef3a854bdbb92fd7eb331687858159a35fe0","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/twisted/txaws/pulls/7/commits","review_comments_url":"https://api.github.com/repos/twisted/txaws/pulls/7/comments","review_comment_url":"https://api.github.com/repos/twisted/txaws/pulls/comments{/number}","comments_url":"https://api.github.com/repos/twisted/txaws/issues/7/comments","statuses_url":"https://api.github.com/repos/twisted/txaws/statuses/5f87bb36a10bbe75ccbf76a0f4a63f9115c984d8","head":{"label":"exarkun:s3-testing","ref":"s3-testing","sha":"5f87bb36a10bbe75ccbf76a0f4a63f9115c984d8","user":{"login":"exarkun","id":254565,"avatar_url":"https://avatars.githubusercontent.com/u/254565?v=3","gravatar_id":"","url":"https://api.github.com/users/exarkun","html_url":"https://github.com/exarkun","followers_url":"https://api.github.com/users/exarkun/followers","following_url":"https://api.github.com/users/exarkun/following{/other_user}","gists_url":"https://api.github.com/users/exarkun/gists{/gist_id}","starred_url":"https://api.github.com/users/exarkun/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/exarkun/subscriptions","organizations_url":"https://api.github.com/users/exarkun/orgs","repos_url":"https://api.github.com/users/exarkun/repos","events_url":"https://api.github.com/users/exarkun/events{/privacy}","received_events_url":"https://api.github.com/users/exarkun/received_events","type":"User","site_admin":false},"repo":{"id":76480275,"name":"txaws","full_name":"exarkun/txaws","owner":{"login":"exarkun","id":254565,"avatar_url":"https://avatars.githubusercontent.com/u/254565?v=3","gravatar_id":"","url":"https://api.github.com/users/exarkun","html_url":"https://github.com/exarkun","followers_url":"https://api.github.com/users/exarkun/followers","following_url":"https://api.github.com/users/exarkun/following{/other_user}","gists_url":"https://api.github.com/users/exarkun/gists{/gist_id}","starred_url":"https://api.github.com/users/exarkun/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/exarkun/subscriptions","organizations_url":"https://api.github.com/users/exarkun/orgs","repos_url":"https://api.github.com/users/exarkun/repos","events_url":"https://api.github.com/users/exarkun/events{/privacy}","received_events_url":"https://api.github.com/users/exarkun/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/exarkun/txaws","description":"Twisted-based Asynchronous Libraries for Amazon Web Services and clouds that support the AWS APIs","fork":true,"url":"https://api.github.com/repos/exarkun/txaws","forks_url":"https://api.github.com/repos/exarkun/txaws/forks","keys_url":"https://api.github.com/repos/exarkun/txaws/keys{/key_id}","collaborators_url":"https://api.github.com/repos/exarkun/txaws/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/exarkun/txaws/teams","hooks_url":"https://api.github.com/repos/exarkun/txaws/hooks","issue_events_url":"https://api.github.com/repos/exarkun/txaws/issues/events{/number}","events_url":"https://api.github.com/repos/exarkun/txaws/events","assignees_url":"https://api.github.com/repos/exarkun/txaws/assignees{/user}","branches_url":"https://api.github.com/repos/exarkun/txaws/branches{/branch}","tags_url":"https://api.github.com/repos/exarkun/txaws/tags","blobs_url":"https://api.github.com/repos/exarkun/txaws/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/exarkun/txaws/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/exarkun/txaws/git/refs{/sha}","trees_url":"https://api.github.com/repos/exarkun/txaws/git/trees{/sha}","statuses_url":"https://api.github.com/repos/exarkun/txaws/statuses/{sha}","languages_url":"https://api.github.com/repos/exarkun/txaws/languages","stargazers_url":"https://api.github.com/repos/exarkun/txaws/stargazers","contributors_url":"https://api.github.com/repos/exarkun/txaws/contributors","subscribers_url":"https://api.github.com/repos/exarkun/txaws/subscribers","subscription_url":"https://api.github.com/repos/exarkun/txaws/subscription","commits_url":"https://api.github.com/repos/exarkun/txaws/commits{/sha}","git_commits_url":"https://api.github.com/repos/exarkun/txaws/git/commits{/sha}","comments_url":"https://api.github.com/repos/exarkun/txaws/comments{/number}","issue_comment_url":"https://api.github.com/repos/exarkun/txaws/issues/comments{/number}","contents_url":"https://api.github.com/repos/exarkun/txaws/contents/{+path}","compare_url":"https://api.github.com/repos/exarkun/txaws/compare/{base}...{head}","merges_url":"https://api.github.com/repos/exarkun/txaws/merges","archive_url":"https://api.github.com/repos/exarkun/txaws/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/exarkun/txaws/downloads","issues_url":"https://api.github.com/repos/exarkun/txaws/issues{/number}","pulls_url":"https://api.github.com/repos/exarkun/txaws/pulls{/number}","milestones_url":"https://api.github.com/repos/exarkun/txaws/milestones{/number}","notifications_url":"https://api.github.com/repos/exarkun/txaws/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/exarkun/txaws/labels{/name}","releases_url":"https://api.github.com/repos/exarkun/txaws/releases{/id}","deployments_url":"https://api.github.com/repos/exarkun/txaws/deployments","created_at":"2016-12-14T17:07:57Z","updated_at":"2016-12-14T17:07:59Z","pushed_at":"2016-12-28T17:56:52Z","git_url":"git://github.com/exarkun/txaws.git","ssh_url":"git@github.com:exarkun/txaws.git","clone_url":"https://github.com/exarkun/txaws.git","svn_url":"https://github.com/exarkun/txaws","homepage":"https://launchpad.net/txaws","size":3847,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"twisted:master","ref":"master","sha":"ea7e42cc807d161d1fadc64fcb552ca470edde2d","user":{"login":"twisted","id":716546,"avatar_url":"https://avatars.githubusercontent.com/u/716546?v=3","gravatar_id":"","url":"https://api.github.com/users/twisted","html_url":"https://github.com/twisted","followers_url":"https://api.github.com/users/twisted/followers","following_url":"https://api.github.com/users/twisted/following{/other_user}","gists_url":"https://api.github.com/users/twisted/gists{/gist_id}","starred_url":"https://api.github.com/users/twisted/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/twisted/subscriptions","organizations_url":"https://api.github.com/users/twisted/orgs","repos_url":"https://api.github.com/users/twisted/repos","events_url":"https://api.github.com/users/twisted/events{/privacy}","received_events_url":"https://api.github.com/users/twisted/received_events","type":"Organization","site_admin":false},"repo":{"id":3238463,"name":"txaws","full_name":"twisted/txaws","owner":{"login":"twisted","id":716546,"avatar_url":"https://avatars.githubusercontent.com/u/716546?v=3","gravatar_id":"","url":"https://api.github.com/users/twisted","html_url":"https://github.com/twisted","followers_url":"https://api.github.com/users/twisted/followers","following_url":"https://api.github.com/users/twisted/following{/other_user}","gists_url":"https://api.github.com/users/twisted/gists{/gist_id}","starred_url":"https://api.github.com/users/twisted/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/twisted/subscriptions","organizations_url":"https://api.github.com/users/twisted/orgs","repos_url":"https://api.github.com/users/twisted/repos","events_url":"https://api.github.com/users/twisted/events{/privacy}","received_events_url":"https://api.github.com/users/twisted/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/twisted/txaws","description":"Twisted-based Asynchronous Libraries for Amazon Web Services and clouds that support the AWS APIs","fork":false,"url":"https://api.github.com/repos/twisted/txaws","forks_url":"https://api.github.com/repos/twisted/txaws/forks","keys_url":"https://api.github.com/repos/twisted/txaws/keys{/key_id}","collaborators_url":"https://api.github.com/repos/twisted/txaws/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/twisted/txaws/teams","hooks_url":"https://api.github.com/repos/twisted/txaws/hooks","issue_events_url":"https://api.github.com/repos/twisted/txaws/issues/events{/number}","events_url":"https://api.github.com/repos/twisted/txaws/events","assignees_url":"https://api.github.com/repos/twisted/txaws/assignees{/user}","branches_url":"https://api.github.com/repos/twisted/txaws/branches{/branch}","tags_url":"https://api.github.com/repos/twisted/txaws/tags","blobs_url":"https://api.github.com/repos/twisted/txaws/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/twisted/txaws/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/twisted/txaws/git/refs{/sha}","trees_url":"https://api.github.com/repos/twisted/txaws/git/trees{/sha}","statuses_url":"https://api.github.com/repos/twisted/txaws/statuses/{sha}","languages_url":"https://api.github.com/repos/twisted/txaws/languages","stargazers_url":"https://api.github.com/repos/twisted/txaws/stargazers","contributors_url":"https://api.github.com/repos/twisted/txaws/contributors","subscribers_url":"https://api.github.com/repos/twisted/txaws/subscribers","subscription_url":"https://api.github.com/repos/twisted/txaws/subscription","commits_url":"https://api.github.com/repos/twisted/txaws/commits{/sha}","git_commits_url":"https://api.github.com/repos/twisted/txaws/git/commits{/sha}","comments_url":"https://api.github.com/repos/twisted/txaws/comments{/number}","issue_comment_url":"https://api.github.com/repos/twisted/txaws/issues/comments{/number}","contents_url":"https://api.github.com/repos/twisted/txaws/contents/{+path}","compare_url":"https://api.github.com/repos/twisted/txaws/compare/{base}...{head}","merges_url":"https://api.github.com/repos/twisted/txaws/merges","archive_url":"https://api.github.com/repos/twisted/txaws/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/twisted/txaws/downloads","issues_url":"https://api.github.com/repos/twisted/txaws/issues{/number}","pulls_url":"https://api.github.com/repos/twisted/txaws/pulls{/number}","milestones_url":"https://api.github.com/repos/twisted/txaws/milestones{/number}","notifications_url":"https://api.github.com/repos/twisted/txaws/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/twisted/txaws/labels{/name}","releases_url":"https://api.github.com/repos/twisted/txaws/releases{/id}","deployments_url":"https://api.github.com/repos/twisted/txaws/deployments","created_at":"2012-01-22T06:41:18Z","updated_at":"2016-12-16T06:39:10Z","pushed_at":"2016-12-28T17:58:44Z","git_url":"git://github.com/twisted/txaws.git","ssh_url":"git@github.com:twisted/txaws.git","clone_url":"https://github.com/twisted/txaws.git","svn_url":"https://github.com/twisted/txaws","homepage":"https://launchpad.net/txaws","size":3883,"stargazers_count":7,"watchers_count":7,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":5,"forks":5,"open_issues":5,"watchers":7,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/twisted/txaws/pulls/7"},"html":{"href":"https://github.com/twisted/txaws/pull/7"},"issue":{"href":"https://api.github.com/repos/twisted/txaws/issues/7"},"comments":{"href":"https://api.github.com/repos/twisted/txaws/issues/7/comments"},"review_comments":{"href":"https://api.github.com/repos/twisted/txaws/pulls/7/comments"},"review_comment":{"href":"https://api.github.com/repos/twisted/txaws/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/twisted/txaws/pulls/7/commits"},"statuses":{"href":"https://api.github.com/repos/twisted/txaws/statuses/5f87bb36a10bbe75ccbf76a0f4a63f9115c984d8"}}}}
{"actor":{"display_login":"mithrandi"}}