{"action":"created","comment":{"url":"https://api.github.com/repos/pydata/patsy/pulls/comments/66570960","id":66570960,"diff_hunk":"@@ -0,0 +1,76 @@\n+from __future__ import print_function\n+\n+import six\n+from six.moves import cPickle as pickle\n+\n+import os\n+import shutil\n+\n+from patsy import EvalFactor\n+\n+PICKE_TESTCASES_ROOTDIR = os.path.join(os.path.dirname(__file__), '..', 'pickle_testcases')\n+\n+pickling_testcases = {\n+    \"evalfactor_simple\": EvalFactor(\"a+b\"),\n+    }\n+\n+def test_pickling_same_version_roundtrips():\n+    for obj in six.itervalues(pickling_testcases):\n+        yield (check_pickling_same_version_roundtrips, obj)\n+\n+def check_pickling_same_version_roundtrips(obj):\n+        assert obj == pickle.loads(pickle.dumps(obj, pickle.HIGHEST_PROTOCOL))\n+\n+def test_pickling_old_versions_still_work():\n+    for (dirpath, dirnames, filenames) in os.walk(PICKE_TESTCASES_ROOTDIR):\n+        for fname in filenames:\n+            if os.path.splitext(fname)[1] == '.pickle':\n+                yield check_pickling_old_versions_still_work, os.path.join(dirpath, fname)\n+\n+def check_pickling_old_versions_still_work(pickle_filename):\n+    testcase_name = os.path.splitext(os.path.basename(pickle_filename))[0]\n+    with open(pickle_filename, 'rb') as f:\n+        # When adding features to a class, it will happen that there is no\n+        # way to make an instance of that version version of that class\n+        # equal to any instance of a previous version. How do we handle\n+        # that?\n+        # Maybe adding a minimum version requirement to each test?\n+        assert pickling_testcases[testcase_name] == pickle.load(f)\n+\n+def test_unpickling_future_gives_sensible_error_msg():\n+    # TODO How would we go about testing this?\n+    pass","path":"patsy/test_pickling.py","position":42,"original_position":42,"commit_id":"12c74716c2fc9da1cace7edd5dec48303198cc7e","original_commit_id":"12c74716c2fc9da1cace7edd5dec48303198cc7e","user":{"login":"njsmith","id":609896,"avatar_url":"https://avatars.githubusercontent.com/u/609896?v=3","gravatar_id":"","url":"https://api.github.com/users/njsmith","html_url":"https://github.com/njsmith","followers_url":"https://api.github.com/users/njsmith/followers","following_url":"https://api.github.com/users/njsmith/following{/other_user}","gists_url":"https://api.github.com/users/njsmith/gists{/gist_id}","starred_url":"https://api.github.com/users/njsmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/njsmith/subscriptions","organizations_url":"https://api.github.com/users/njsmith/orgs","repos_url":"https://api.github.com/users/njsmith/repos","events_url":"https://api.github.com/users/njsmith/events{/privacy}","received_events_url":"https://api.github.com/users/njsmith/received_events","type":"User","site_admin":false},"body":"I guess just test that we get a `PickleFromTheFuture` exception, or whatever it is we decide to call that?","created_at":"2016-06-10T07:19:05Z","updated_at":"2016-06-10T07:19:05Z","html_url":"https://github.com/pydata/patsy/pull/67#discussion_r66570960","pull_request_url":"https://api.github.com/repos/pydata/patsy/pulls/67","_links":{"self":{"href":"https://api.github.com/repos/pydata/patsy/pulls/comments/66570960"},"html":{"href":"https://github.com/pydata/patsy/pull/67#discussion_r66570960"},"pull_request":{"href":"https://api.github.com/repos/pydata/patsy/pulls/67"}}},"pull_request":{"url":"https://api.github.com/repos/pydata/patsy/pulls/67","id":35895643,"html_url":"https://github.com/pydata/patsy/pull/67","diff_url":"https://github.com/pydata/patsy/pull/67.diff","patch_url":"https://github.com/pydata/patsy/pull/67.patch","issue_url":"https://api.github.com/repos/pydata/patsy/issues/67","number":67,"state":"open","locked":false,"title":"Patsy pickling","user":{"login":"chrish42","id":632858,"avatar_url":"https://avatars.githubusercontent.com/u/632858?v=3","gravatar_id":"","url":"https://api.github.com/users/chrish42","html_url":"https://github.com/chrish42","followers_url":"https://api.github.com/users/chrish42/followers","following_url":"https://api.github.com/users/chrish42/following{/other_user}","gists_url":"https://api.github.com/users/chrish42/gists{/gist_id}","starred_url":"https://api.github.com/users/chrish42/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrish42/subscriptions","organizations_url":"https://api.github.com/users/chrish42/orgs","repos_url":"https://api.github.com/users/chrish42/repos","events_url":"https://api.github.com/users/chrish42/events{/privacy}","received_events_url":"https://api.github.com/users/chrish42/received_events","type":"User","site_admin":false},"body":"Branch for the rest of the work required to make patsy pickles work well. At a high level, we need to implement `__getstate__` and `__setstate__` for relevant classes (easy), and add a testing framework (that is easy to use) that makes sure that pickles created with one version of patsy continue to work with newer versions (harder).\r\n\r\nPickling testsuite:\r\n- [ ] Create dict of pickling tests, with the key being the test name, and the value being an object of the above list, in a given configuration.\r\n- [ ] Create a test helper that iterates through the above, saving a pickle for each in a file `pickles/vX.y/test_name.pickle` (where \"test_name\" is the key in the dict for the corresponding object). This program would be run before every release (with the version number of the release as an argument), to create a new set of pickle files for that release. Said pickle files would be added to the source repository.\r\n- [ ] Create an adapter that makes nosetests iterate through all these objects, and check that they compare equal to all the versions of their corresponding pickles.\r\n\r\nPickle support:\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `EvalFactor`\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `LookupFactor`\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `Term`\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `EvalEnvironment`\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for the stateful transform classes\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `ContrastMatrix`\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `DesignInfo`\r\n- [ ] Implement `__getstate__` and `__setstate__` methods for `DesignMatrix`","created_at":"2015-05-21T02:51:50Z","updated_at":"2016-06-10T07:19:05Z","closed_at":null,"merged_at":null,"merge_commit_sha":"272c69f66775812416460f5e7db3f998bed71cb9","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/pydata/patsy/pulls/67/commits","review_comments_url":"https://api.github.com/repos/pydata/patsy/pulls/67/comments","review_comment_url":"https://api.github.com/repos/pydata/patsy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pydata/patsy/issues/67/comments","statuses_url":"https://api.github.com/repos/pydata/patsy/statuses/12c74716c2fc9da1cace7edd5dec48303198cc7e","head":{"label":"chrish42:patsy-pickles","ref":"patsy-pickles","sha":"12c74716c2fc9da1cace7edd5dec48303198cc7e","user":{"login":"chrish42","id":632858,"avatar_url":"https://avatars.githubusercontent.com/u/632858?v=3","gravatar_id":"","url":"https://api.github.com/users/chrish42","html_url":"https://github.com/chrish42","followers_url":"https://api.github.com/users/chrish42/followers","following_url":"https://api.github.com/users/chrish42/following{/other_user}","gists_url":"https://api.github.com/users/chrish42/gists{/gist_id}","starred_url":"https://api.github.com/users/chrish42/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrish42/subscriptions","organizations_url":"https://api.github.com/users/chrish42/orgs","repos_url":"https://api.github.com/users/chrish42/repos","events_url":"https://api.github.com/users/chrish42/events{/privacy}","received_events_url":"https://api.github.com/users/chrish42/received_events","type":"User","site_admin":false},"repo":{"id":33874321,"name":"patsy","full_name":"chrish42/patsy","owner":{"login":"chrish42","id":632858,"avatar_url":"https://avatars.githubusercontent.com/u/632858?v=3","gravatar_id":"","url":"https://api.github.com/users/chrish42","html_url":"https://github.com/chrish42","followers_url":"https://api.github.com/users/chrish42/followers","following_url":"https://api.github.com/users/chrish42/following{/other_user}","gists_url":"https://api.github.com/users/chrish42/gists{/gist_id}","starred_url":"https://api.github.com/users/chrish42/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrish42/subscriptions","organizations_url":"https://api.github.com/users/chrish42/orgs","repos_url":"https://api.github.com/users/chrish42/repos","events_url":"https://api.github.com/users/chrish42/events{/privacy}","received_events_url":"https://api.github.com/users/chrish42/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/chrish42/patsy","description":"Describing statistical models in Python using symbolic formulas","fork":true,"url":"https://api.github.com/repos/chrish42/patsy","forks_url":"https://api.github.com/repos/chrish42/patsy/forks","keys_url":"https://api.github.com/repos/chrish42/patsy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/chrish42/patsy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/chrish42/patsy/teams","hooks_url":"https://api.github.com/repos/chrish42/patsy/hooks","issue_events_url":"https://api.github.com/repos/chrish42/patsy/issues/events{/number}","events_url":"https://api.github.com/repos/chrish42/patsy/events","assignees_url":"https://api.github.com/repos/chrish42/patsy/assignees{/user}","branches_url":"https://api.github.com/repos/chrish42/patsy/branches{/branch}","tags_url":"https://api.github.com/repos/chrish42/patsy/tags","blobs_url":"https://api.github.com/repos/chrish42/patsy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/chrish42/patsy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/chrish42/patsy/git/refs{/sha}","trees_url":"https://api.github.com/repos/chrish42/patsy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/chrish42/patsy/statuses/{sha}","languages_url":"https://api.github.com/repos/chrish42/patsy/languages","stargazers_url":"https://api.github.com/repos/chrish42/patsy/stargazers","contributors_url":"https://api.github.com/repos/chrish42/patsy/contributors","subscribers_url":"https://api.github.com/repos/chrish42/patsy/subscribers","subscription_url":"https://api.github.com/repos/chrish42/patsy/subscription","commits_url":"https://api.github.com/repos/chrish42/patsy/commits{/sha}","git_commits_url":"https://api.github.com/repos/chrish42/patsy/git/commits{/sha}","comments_url":"https://api.github.com/repos/chrish42/patsy/comments{/number}","issue_comment_url":"https://api.github.com/repos/chrish42/patsy/issues/comments{/number}","contents_url":"https://api.github.com/repos/chrish42/patsy/contents/{+path}","compare_url":"https://api.github.com/repos/chrish42/patsy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/chrish42/patsy/merges","archive_url":"https://api.github.com/repos/chrish42/patsy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/chrish42/patsy/downloads","issues_url":"https://api.github.com/repos/chrish42/patsy/issues{/number}","pulls_url":"https://api.github.com/repos/chrish42/patsy/pulls{/number}","milestones_url":"https://api.github.com/repos/chrish42/patsy/milestones{/number}","notifications_url":"https://api.github.com/repos/chrish42/patsy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/chrish42/patsy/labels{/name}","releases_url":"https://api.github.com/repos/chrish42/patsy/releases{/id}","deployments_url":"https://api.github.com/repos/chrish42/patsy/deployments","created_at":"2015-04-13T14:47:00Z","updated_at":"2016-06-02T20:09:41Z","pushed_at":"2016-06-06T00:07:20Z","git_url":"git://github.com/chrish42/patsy.git","ssh_url":"git@github.com:chrish42/patsy.git","clone_url":"https://github.com/chrish42/patsy.git","svn_url":"https://github.com/chrish42/patsy","homepage":null,"size":1776,"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":"pydata:master","ref":"master","sha":"dd6b3ba05ce34c9ed7afe86d4e6df9b838064b44","user":{"login":"pydata","id":1284191,"avatar_url":"https://avatars.githubusercontent.com/u/1284191?v=3","gravatar_id":"","url":"https://api.github.com/users/pydata","html_url":"https://github.com/pydata","followers_url":"https://api.github.com/users/pydata/followers","following_url":"https://api.github.com/users/pydata/following{/other_user}","gists_url":"https://api.github.com/users/pydata/gists{/gist_id}","starred_url":"https://api.github.com/users/pydata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pydata/subscriptions","organizations_url":"https://api.github.com/users/pydata/orgs","repos_url":"https://api.github.com/users/pydata/repos","events_url":"https://api.github.com/users/pydata/events{/privacy}","received_events_url":"https://api.github.com/users/pydata/received_events","type":"Organization","site_admin":false},"repo":{"id":4973632,"name":"patsy","full_name":"pydata/patsy","owner":{"login":"pydata","id":1284191,"avatar_url":"https://avatars.githubusercontent.com/u/1284191?v=3","gravatar_id":"","url":"https://api.github.com/users/pydata","html_url":"https://github.com/pydata","followers_url":"https://api.github.com/users/pydata/followers","following_url":"https://api.github.com/users/pydata/following{/other_user}","gists_url":"https://api.github.com/users/pydata/gists{/gist_id}","starred_url":"https://api.github.com/users/pydata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pydata/subscriptions","organizations_url":"https://api.github.com/users/pydata/orgs","repos_url":"https://api.github.com/users/pydata/repos","events_url":"https://api.github.com/users/pydata/events{/privacy}","received_events_url":"https://api.github.com/users/pydata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pydata/patsy","description":"Describing statistical models in Python using symbolic formulas","fork":false,"url":"https://api.github.com/repos/pydata/patsy","forks_url":"https://api.github.com/repos/pydata/patsy/forks","keys_url":"https://api.github.com/repos/pydata/patsy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pydata/patsy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pydata/patsy/teams","hooks_url":"https://api.github.com/repos/pydata/patsy/hooks","issue_events_url":"https://api.github.com/repos/pydata/patsy/issues/events{/number}","events_url":"https://api.github.com/repos/pydata/patsy/events","assignees_url":"https://api.github.com/repos/pydata/patsy/assignees{/user}","branches_url":"https://api.github.com/repos/pydata/patsy/branches{/branch}","tags_url":"https://api.github.com/repos/pydata/patsy/tags","blobs_url":"https://api.github.com/repos/pydata/patsy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pydata/patsy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pydata/patsy/git/refs{/sha}","trees_url":"https://api.github.com/repos/pydata/patsy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pydata/patsy/statuses/{sha}","languages_url":"https://api.github.com/repos/pydata/patsy/languages","stargazers_url":"https://api.github.com/repos/pydata/patsy/stargazers","contributors_url":"https://api.github.com/repos/pydata/patsy/contributors","subscribers_url":"https://api.github.com/repos/pydata/patsy/subscribers","subscription_url":"https://api.github.com/repos/pydata/patsy/subscription","commits_url":"https://api.github.com/repos/pydata/patsy/commits{/sha}","git_commits_url":"https://api.github.com/repos/pydata/patsy/git/commits{/sha}","comments_url":"https://api.github.com/repos/pydata/patsy/comments{/number}","issue_comment_url":"https://api.github.com/repos/pydata/patsy/issues/comments{/number}","contents_url":"https://api.github.com/repos/pydata/patsy/contents/{+path}","compare_url":"https://api.github.com/repos/pydata/patsy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pydata/patsy/merges","archive_url":"https://api.github.com/repos/pydata/patsy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pydata/patsy/downloads","issues_url":"https://api.github.com/repos/pydata/patsy/issues{/number}","pulls_url":"https://api.github.com/repos/pydata/patsy/pulls{/number}","milestones_url":"https://api.github.com/repos/pydata/patsy/milestones{/number}","notifications_url":"https://api.github.com/repos/pydata/patsy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pydata/patsy/labels{/name}","releases_url":"https://api.github.com/repos/pydata/patsy/releases{/id}","deployments_url":"https://api.github.com/repos/pydata/patsy/deployments","created_at":"2012-07-10T12:30:06Z","updated_at":"2016-06-06T09:13:13Z","pushed_at":"2016-06-06T00:07:20Z","git_url":"git://github.com/pydata/patsy.git","ssh_url":"git@github.com:pydata/patsy.git","clone_url":"https://github.com/pydata/patsy.git","svn_url":"https://github.com/pydata/patsy","homepage":null,"size":1823,"stargazers_count":300,"watchers_count":300,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":38,"mirror_url":null,"open_issues_count":33,"forks":38,"open_issues":33,"watchers":300,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pydata/patsy/pulls/67"},"html":{"href":"https://github.com/pydata/patsy/pull/67"},"issue":{"href":"https://api.github.com/repos/pydata/patsy/issues/67"},"comments":{"href":"https://api.github.com/repos/pydata/patsy/issues/67/comments"},"review_comments":{"href":"https://api.github.com/repos/pydata/patsy/pulls/67/comments"},"review_comment":{"href":"https://api.github.com/repos/pydata/patsy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pydata/patsy/pulls/67/commits"},"statuses":{"href":"https://api.github.com/repos/pydata/patsy/statuses/12c74716c2fc9da1cace7edd5dec48303198cc7e"}}}}
{"actor":{"display_login":"njsmith"}}