{"action":"created","comment":{"url":"https://api.github.com/repos/zopefoundation/persistent/pulls/comments/30610114","id":30610114,"diff_hunk":"@@ -331,36 +361,81 @@ def __reduce__(self):\n     def _p_activate(self):\n         \"\"\" See IPersistent.\n         \"\"\"\n-        before = self.__flags\n-        if self.__flags is None or self._p_state < 0: # Only do this if we're a ghost\n-            self.__flags = 0\n-            if self.__jar is not None and self.__oid is not None:\n-                try:\n-                    self.__jar.setstate(self)\n-                except:\n-                    self.__flags = before\n-                    raise\n+        oga = _OGA\n+        before = oga(self, '_Persistent__flags')\n+        if before is None: # Only do this if we're a ghost\n+            # Begin by marking up-to-date in case we bail early\n+            _OSA(self, '_Persistent__flags', 0)\n+            jar = oga(self, '_Persistent__jar')\n+            if jar is None:\n+                return\n+            oid = oga(self, '_Persistent__oid')\n+            if oid is None:\n+                return\n+\n+            # If we're actually going to execute a set-state,\n+            # mark as changed to prevent any recursive call\n+            # (actually, our earlier check that we're a ghost should\n+            # prevent this, but the C implementation sets it to changed\n+            # while calling jar.setstate, and this is observable to clients).\n+            # The main point of this is to prevent changes made during\n+            # setstate from registering the object with the jar.\n+            _OSA(self, '_Persistent__flags', CHANGED)\n+            try:\n+                jar.setstate(self)\n+            except:\n+                _OSA(self, '_Persistent__flags', before)\n+                raise\n+            else:\n+                # If we succeed, no matter what the implementation\n+                # of setstate did, mark ourself as up-to-date. The\n+                # C implementation unconditionally does this.\n+                _OSA(self, '_Persistent__flags', 0) # up-to-date\n+\n+    # In the C implementation, _p_invalidate winds up calling\n+    # _p_deactivate. There are ZODB tests that depend on this;\n+    # it's not documented but there may be code in the wild\n+    # that does as well","path":"persistent/persistence.py","position":263,"original_position":269,"commit_id":"248244954dde1d7bbb14c63ce4119be64e675a93","original_commit_id":"d4d19019af1da75a276afe632decbaae5eacc8e5","user":{"login":"jamadden","id":1256082,"avatar_url":"https://avatars.githubusercontent.com/u/1256082?v=3","gravatar_id":"","url":"https://api.github.com/users/jamadden","html_url":"https://github.com/jamadden","followers_url":"https://api.github.com/users/jamadden/followers","following_url":"https://api.github.com/users/jamadden/following{/other_user}","gists_url":"https://api.github.com/users/jamadden/gists{/gist_id}","starred_url":"https://api.github.com/users/jamadden/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamadden/subscriptions","organizations_url":"https://api.github.com/users/jamadden/orgs","repos_url":"https://api.github.com/users/jamadden/repos","events_url":"https://api.github.com/users/jamadden/events{/privacy}","received_events_url":"https://api.github.com/users/jamadden/received_events","type":"User","site_admin":false},"body":"Here's one that fails: https://github.com/zopefoundation/ZODB/blob/master/src/ZODB/tests/test_cache.py#L204\r\n\r\nYes, it is a doctest, but it is asserting about the desired outcome: Objects that are modified during a transaction that is then aborted should be deactivated. Unless `_p_invalidate` calls `_p_deactivate`, that's not necessarily true. And implementations of IPersistent are explicitly allowed to override `_p_deactivate` to prevent deactivations from occurring.","created_at":"2015-05-19T15:16:16Z","updated_at":"2015-05-19T15:16:16Z","html_url":"https://github.com/zopefoundation/persistent/pull/20#discussion_r30610114","pull_request_url":"https://api.github.com/repos/zopefoundation/persistent/pulls/20","_links":{"self":{"href":"https://api.github.com/repos/zopefoundation/persistent/pulls/comments/30610114"},"html":{"href":"https://github.com/zopefoundation/persistent/pull/20#discussion_r30610114"},"pull_request":{"href":"https://api.github.com/repos/zopefoundation/persistent/pulls/20"}}},"pull_request":{"url":"https://api.github.com/repos/zopefoundation/persistent/pulls/20","id":32916799,"html_url":"https://github.com/zopefoundation/persistent/pull/20","diff_url":"https://github.com/zopefoundation/persistent/pull/20.diff","patch_url":"https://github.com/zopefoundation/persistent/pull/20.patch","issue_url":"https://api.github.com/repos/zopefoundation/persistent/issues/20","number":20,"state":"open","locked":false,"title":"Support for ZODB on PyPy","user":{"login":"jamadden","id":1256082,"avatar_url":"https://avatars.githubusercontent.com/u/1256082?v=3","gravatar_id":"","url":"https://api.github.com/users/jamadden","html_url":"https://github.com/jamadden","followers_url":"https://api.github.com/users/jamadden/followers","following_url":"https://api.github.com/users/jamadden/following{/other_user}","gists_url":"https://api.github.com/users/jamadden/gists{/gist_id}","starred_url":"https://api.github.com/users/jamadden/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamadden/subscriptions","organizations_url":"https://api.github.com/users/jamadden/orgs","repos_url":"https://api.github.com/users/jamadden/repos","events_url":"https://api.github.com/users/jamadden/events{/privacy}","received_events_url":"https://api.github.com/users/jamadden/received_events","type":"User","site_admin":false},"body":"This PR brings the necessary changes to make the complete set of ZODB unit and functional tests pass (1410 in total). (See zopefoundation/ZODB#33)\r\n\r\nMostly this consisted of making the Python implementation more closely mirror the C implementation. A few notable things:\r\n\r\n- The PickleCache now supports the byte-size limit and the `total_estimated_size`.\r\n- The PickleCache now ghostifies objects during a GC, and handles objects that either deliberately or accidentally override `_p_invalidate` and `_p_activate` without calling super (ZODB's Blob is an example of a deliberate use of both).\r\n- The Persistent class now calls the same methods in the same order as the C class; code in the wild could easily depend on this accidentally. It's also safe to use many of the `_p` attributes in more circumstances than before (ZODB's Broken class relies on this).\r\n- A few restrictions had to be relaxed to match the C implementation (e.g., allowing arbitrary values for `_p_oid`, something that could easily crop up in test code in the wild like it did in ZODB) ; a few restrictions had to be added to match the C implementation (e.g., requiring objects in the PickleCache to have a jar).\r\n\r\nAll of the existing test cases should pass with very minor changes. I've already added a few test cases for the easier things and will see about adding a few more if I can extract them from ZODB in a reasonable form to try to get the coverage a bit higher (although it's not too bad as it is). Some of the scenarios are fairly complex though so it may be difficult to separate them. (Although, especially in the case of the C implementations it seems like many of the test cases still effectively live in ZODB.)\r\n\r\nI'm opening this PR a bit early because I know it's large and involves some changes in the way the cache and Persistent objects have to interact, so I'm open to any feedback and suggestions. Plus I probably can't open a PR for the ZODB changes until this gets merged and released :)\r\n\r\n```\r\nName                     Stmts   Miss Branch BrMiss  Cover   Missing\r\n--------------------------------------------------------------------\r\npersistent                  21     11      0      0    48%   24-46\r\npersistent._compat           1      0      0      0   100%\r\npersistent.dict              1      0      0      0   100%\r\npersistent.interfaces        9      3      0      0    67%   23-25\r\npersistent.list             60      0      0      0   100%\r\npersistent.mapping          44      0      4      0   100%\r\npersistent.persistence     297      0    150      0   100%\r\npersistent.picklecache     223     15    106     12    92%   65-67, 100, 118, 124, 165, 295-305, 399-401\r\npersistent.timestamp       125      0     14      0   100%\r\npersistent.timestamp       125      0     14      0   100%\r\npersistent.wref             68      0     18      0   100%\r\n--------------------------------------------------------------------\r\nTOTAL                      972     31    308     14    96%\r\n```","created_at":"2015-04-09T00:58:26Z","updated_at":"2015-05-19T15:16:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b0e8a0e53b1b6a8072f981ddf5fa7254d21ae670","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/zopefoundation/persistent/pulls/20/commits","review_comments_url":"https://api.github.com/repos/zopefoundation/persistent/pulls/20/comments","review_comment_url":"https://api.github.com/repos/zopefoundation/persistent/pulls/comments{/number}","comments_url":"https://api.github.com/repos/zopefoundation/persistent/issues/20/comments","statuses_url":"https://api.github.com/repos/zopefoundation/persistent/statuses/248244954dde1d7bbb14c63ce4119be64e675a93","head":{"label":"NextThought:zodb-on-pypy-support","ref":"zodb-on-pypy-support","sha":"248244954dde1d7bbb14c63ce4119be64e675a93","user":{"login":"NextThought","id":2072248,"avatar_url":"https://avatars.githubusercontent.com/u/2072248?v=3","gravatar_id":"","url":"https://api.github.com/users/NextThought","html_url":"https://github.com/NextThought","followers_url":"https://api.github.com/users/NextThought/followers","following_url":"https://api.github.com/users/NextThought/following{/other_user}","gists_url":"https://api.github.com/users/NextThought/gists{/gist_id}","starred_url":"https://api.github.com/users/NextThought/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NextThought/subscriptions","organizations_url":"https://api.github.com/users/NextThought/orgs","repos_url":"https://api.github.com/users/NextThought/repos","events_url":"https://api.github.com/users/NextThought/events{/privacy}","received_events_url":"https://api.github.com/users/NextThought/received_events","type":"Organization","site_admin":false},"repo":{"id":16959884,"name":"persistent","full_name":"NextThought/persistent","owner":{"login":"NextThought","id":2072248,"avatar_url":"https://avatars.githubusercontent.com/u/2072248?v=3","gravatar_id":"","url":"https://api.github.com/users/NextThought","html_url":"https://github.com/NextThought","followers_url":"https://api.github.com/users/NextThought/followers","following_url":"https://api.github.com/users/NextThought/following{/other_user}","gists_url":"https://api.github.com/users/NextThought/gists{/gist_id}","starred_url":"https://api.github.com/users/NextThought/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/NextThought/subscriptions","organizations_url":"https://api.github.com/users/NextThought/orgs","repos_url":"https://api.github.com/users/NextThought/repos","events_url":"https://api.github.com/users/NextThought/events{/privacy}","received_events_url":"https://api.github.com/users/NextThought/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/NextThought/persistent","description":"automatic persistence for Python objects","fork":true,"url":"https://api.github.com/repos/NextThought/persistent","forks_url":"https://api.github.com/repos/NextThought/persistent/forks","keys_url":"https://api.github.com/repos/NextThought/persistent/keys{/key_id}","collaborators_url":"https://api.github.com/repos/NextThought/persistent/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/NextThought/persistent/teams","hooks_url":"https://api.github.com/repos/NextThought/persistent/hooks","issue_events_url":"https://api.github.com/repos/NextThought/persistent/issues/events{/number}","events_url":"https://api.github.com/repos/NextThought/persistent/events","assignees_url":"https://api.github.com/repos/NextThought/persistent/assignees{/user}","branches_url":"https://api.github.com/repos/NextThought/persistent/branches{/branch}","tags_url":"https://api.github.com/repos/NextThought/persistent/tags","blobs_url":"https://api.github.com/repos/NextThought/persistent/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/NextThought/persistent/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/NextThought/persistent/git/refs{/sha}","trees_url":"https://api.github.com/repos/NextThought/persistent/git/trees{/sha}","statuses_url":"https://api.github.com/repos/NextThought/persistent/statuses/{sha}","languages_url":"https://api.github.com/repos/NextThought/persistent/languages","stargazers_url":"https://api.github.com/repos/NextThought/persistent/stargazers","contributors_url":"https://api.github.com/repos/NextThought/persistent/contributors","subscribers_url":"https://api.github.com/repos/NextThought/persistent/subscribers","subscription_url":"https://api.github.com/repos/NextThought/persistent/subscription","commits_url":"https://api.github.com/repos/NextThought/persistent/commits{/sha}","git_commits_url":"https://api.github.com/repos/NextThought/persistent/git/commits{/sha}","comments_url":"https://api.github.com/repos/NextThought/persistent/comments{/number}","issue_comment_url":"https://api.github.com/repos/NextThought/persistent/issues/comments{/number}","contents_url":"https://api.github.com/repos/NextThought/persistent/contents/{+path}","compare_url":"https://api.github.com/repos/NextThought/persistent/compare/{base}...{head}","merges_url":"https://api.github.com/repos/NextThought/persistent/merges","archive_url":"https://api.github.com/repos/NextThought/persistent/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/NextThought/persistent/downloads","issues_url":"https://api.github.com/repos/NextThought/persistent/issues{/number}","pulls_url":"https://api.github.com/repos/NextThought/persistent/pulls{/number}","milestones_url":"https://api.github.com/repos/NextThought/persistent/milestones{/number}","notifications_url":"https://api.github.com/repos/NextThought/persistent/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/NextThought/persistent/labels{/name}","releases_url":"https://api.github.com/repos/NextThought/persistent/releases{/id}","created_at":"2014-02-18T18:53:24Z","updated_at":"2015-04-09T13:54:44Z","pushed_at":"2015-05-19T14:34:53Z","git_url":"git://github.com/NextThought/persistent.git","ssh_url":"git@github.com:NextThought/persistent.git","clone_url":"https://github.com/NextThought/persistent.git","svn_url":"https://github.com/NextThought/persistent","homepage":"https://pypi.python.org/pypi/persistent","size":656,"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":"zopefoundation:master","ref":"master","sha":"7f673b5397fbfe423bec612c1f42f57eac0e9634","user":{"login":"zopefoundation","id":1163184,"avatar_url":"https://avatars.githubusercontent.com/u/1163184?v=3","gravatar_id":"","url":"https://api.github.com/users/zopefoundation","html_url":"https://github.com/zopefoundation","followers_url":"https://api.github.com/users/zopefoundation/followers","following_url":"https://api.github.com/users/zopefoundation/following{/other_user}","gists_url":"https://api.github.com/users/zopefoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/zopefoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zopefoundation/subscriptions","organizations_url":"https://api.github.com/users/zopefoundation/orgs","repos_url":"https://api.github.com/users/zopefoundation/repos","events_url":"https://api.github.com/users/zopefoundation/events{/privacy}","received_events_url":"https://api.github.com/users/zopefoundation/received_events","type":"Organization","site_admin":false},"repo":{"id":7458003,"name":"persistent","full_name":"zopefoundation/persistent","owner":{"login":"zopefoundation","id":1163184,"avatar_url":"https://avatars.githubusercontent.com/u/1163184?v=3","gravatar_id":"","url":"https://api.github.com/users/zopefoundation","html_url":"https://github.com/zopefoundation","followers_url":"https://api.github.com/users/zopefoundation/followers","following_url":"https://api.github.com/users/zopefoundation/following{/other_user}","gists_url":"https://api.github.com/users/zopefoundation/gists{/gist_id}","starred_url":"https://api.github.com/users/zopefoundation/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zopefoundation/subscriptions","organizations_url":"https://api.github.com/users/zopefoundation/orgs","repos_url":"https://api.github.com/users/zopefoundation/repos","events_url":"https://api.github.com/users/zopefoundation/events{/privacy}","received_events_url":"https://api.github.com/users/zopefoundation/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/zopefoundation/persistent","description":"automatic persistence for Python objects","fork":false,"url":"https://api.github.com/repos/zopefoundation/persistent","forks_url":"https://api.github.com/repos/zopefoundation/persistent/forks","keys_url":"https://api.github.com/repos/zopefoundation/persistent/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zopefoundation/persistent/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zopefoundation/persistent/teams","hooks_url":"https://api.github.com/repos/zopefoundation/persistent/hooks","issue_events_url":"https://api.github.com/repos/zopefoundation/persistent/issues/events{/number}","events_url":"https://api.github.com/repos/zopefoundation/persistent/events","assignees_url":"https://api.github.com/repos/zopefoundation/persistent/assignees{/user}","branches_url":"https://api.github.com/repos/zopefoundation/persistent/branches{/branch}","tags_url":"https://api.github.com/repos/zopefoundation/persistent/tags","blobs_url":"https://api.github.com/repos/zopefoundation/persistent/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zopefoundation/persistent/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zopefoundation/persistent/git/refs{/sha}","trees_url":"https://api.github.com/repos/zopefoundation/persistent/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zopefoundation/persistent/statuses/{sha}","languages_url":"https://api.github.com/repos/zopefoundation/persistent/languages","stargazers_url":"https://api.github.com/repos/zopefoundation/persistent/stargazers","contributors_url":"https://api.github.com/repos/zopefoundation/persistent/contributors","subscribers_url":"https://api.github.com/repos/zopefoundation/persistent/subscribers","subscription_url":"https://api.github.com/repos/zopefoundation/persistent/subscription","commits_url":"https://api.github.com/repos/zopefoundation/persistent/commits{/sha}","git_commits_url":"https://api.github.com/repos/zopefoundation/persistent/git/commits{/sha}","comments_url":"https://api.github.com/repos/zopefoundation/persistent/comments{/number}","issue_comment_url":"https://api.github.com/repos/zopefoundation/persistent/issues/comments{/number}","contents_url":"https://api.github.com/repos/zopefoundation/persistent/contents/{+path}","compare_url":"https://api.github.com/repos/zopefoundation/persistent/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zopefoundation/persistent/merges","archive_url":"https://api.github.com/repos/zopefoundation/persistent/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zopefoundation/persistent/downloads","issues_url":"https://api.github.com/repos/zopefoundation/persistent/issues{/number}","pulls_url":"https://api.github.com/repos/zopefoundation/persistent/pulls{/number}","milestones_url":"https://api.github.com/repos/zopefoundation/persistent/milestones{/number}","notifications_url":"https://api.github.com/repos/zopefoundation/persistent/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zopefoundation/persistent/labels{/name}","releases_url":"https://api.github.com/repos/zopefoundation/persistent/releases{/id}","created_at":"2013-01-05T17:32:44Z","updated_at":"2015-04-10T14:38:49Z","pushed_at":"2015-05-19T14:34:54Z","git_url":"git://github.com/zopefoundation/persistent.git","ssh_url":"git@github.com:zopefoundation/persistent.git","clone_url":"https://github.com/zopefoundation/persistent.git","svn_url":"https://github.com/zopefoundation/persistent","homepage":"https://pypi.python.org/pypi/persistent","size":795,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":3,"forks":5,"open_issues":3,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/zopefoundation/persistent/pulls/20"},"html":{"href":"https://github.com/zopefoundation/persistent/pull/20"},"issue":{"href":"https://api.github.com/repos/zopefoundation/persistent/issues/20"},"comments":{"href":"https://api.github.com/repos/zopefoundation/persistent/issues/20/comments"},"review_comments":{"href":"https://api.github.com/repos/zopefoundation/persistent/pulls/20/comments"},"review_comment":{"href":"https://api.github.com/repos/zopefoundation/persistent/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/zopefoundation/persistent/pulls/20/commits"},"statuses":{"href":"https://api.github.com/repos/zopefoundation/persistent/statuses/248244954dde1d7bbb14c63ce4119be64e675a93"}}}}