{"action":"created","comment":{"url":"https://api.github.com/repos/soft-matter/slicerator/pulls/comments/40805996","id":40805996,"diff_hunk":"@@ -88,80 +226,85 @@ def __iter__(self):\n     def __len__(self):\n         return self._len\n \n-    def __getattr__(self, key):\n-        # Remember this only gets called if __getattribute__ raises an\n-        # AttributeError. Try the ancestor object.\n-        attr = getattr(self._ancestor, key)\n-        if isinstance(attr, Slicerator):\n-            return Slicerator(attr, self.indices, len(self))\n-        else:\n-            return attr\n-\n     def __getitem__(self, key):\n         \"\"\"for data access\"\"\"\n-        _len = len(self)\n-        abs_indices = self.indices\n-\n-        if isinstance(key, slice):\n-            # if input is a slice, return another Slicerator\n-            start, stop, step = key.indices(_len)\n-            rel_indices = range(start, stop, step)\n-            new_length = len(rel_indices)\n-            indices = _index_generator(rel_indices, abs_indices)\n-            return Slicerator(self._ancestor, indices, new_length)\n-        elif isinstance(key, collections.Iterable):\n-            # if the input is an iterable, doing 'fancy' indexing\n-            if hasattr(key, '__array__') and hasattr(key, 'dtype'):\n-                if key.dtype == bool:\n-                    # if we have a bool array, set up masking but defer\n-                    # the actual computation, returning another Slicerator\n-                    nums = range(len(self))\n-                    # This next line fakes up numpy's bool masking without\n-                    # importing numpy.\n-                    rel_indices = [x for x, y in zip(nums, key) if y]\n-                    indices = _index_generator(rel_indices, abs_indices)\n-                    new_length = sum(key)\n-                    return Slicerator(self._ancestor, indices, new_length)\n-            try:\n-                new_length = len(key)\n-            except TypeError:\n-                # The key is a generator; return a plain old generator.\n-                # Without knowing the length of the *key*,\n-                # we can't give a Slicerator\n-                # Also it cannot be checked if values are in range.\n-                gen = (self[_k if _k >= 0 else _len + _k] for _k in key)\n-                return gen\n-            else:\n-                # The key is a list of in-range values. Build another\n-                # Slicerator, again deferring computation.\n-                if any(_k < -_len or _k >= _len for _k in key):\n-                    raise IndexError(\"Keys out of range\")\n-                rel_indices = ((_k if _k >= 0 else _len + _k) for _k in key)\n-                indices = _index_generator(rel_indices, abs_indices)\n-                return Slicerator(self._ancestor, indices, new_length)\n+        if not (isinstance(key, slice) or\n+                isinstance(key, collections.Iterable)):\n+            return self._get(self._map_index(key))\n         else:\n-            if key < -_len or key >= _len:\n-                raise IndexError(\"Key out of range\")\n-            try:\n-                abs_key = self._indices[key]\n-            except TypeError:\n-                key = key if key >= 0 else _len + key\n-                for _, i in zip(range(key + 1), self.indices):\n-                    abs_key = i\n-            return self._get(abs_key)\n+            rel_indices, new_length = key_to_indices(key, len(self))\n+            if new_length is None:\n+                return (self[k] for k in rel_indices)\n+            indices = _index_generator(rel_indices, self.indices)\n+            return Slicerator(self._ancestor, indices,\n+                              new_length, self.propagated_attrs)\n+\n+    def __getattr__(self, name):\n+        # to avoid infinite recursion, always check if public field is there\n+        if 'propagated_attrs' not in self.__dict__:\n+            self.propagated_attrs = []\n+        if name in self.propagated_attrs:\n+            attr = getattr(self._ancestor, name)\n+            if hasattr(attr, '_indexed'):\n+                return SliceableAttribute(self, getattr(self._ancestor, name))\n+            else:\n+                return getattr(self._ancestor, name)\n+        raise AttributeError\n \n     def __getstate__(self):\n         # When serializing, return a list of the sliced and processed data\n+        # Any exposed attrs are lost.\n         return [self._get(key) for key in self.indices]","path":"slicerator.py","position":305,"original_position":305,"commit_id":"eeef867f7538879a76fec24a992e66e2feb79f39","original_commit_id":"eeef867f7538879a76fec24a992e66e2feb79f39","user":{"login":"caspervdw","id":8937919,"avatar_url":"https://avatars.githubusercontent.com/u/8937919?v=3","gravatar_id":"","url":"https://api.github.com/users/caspervdw","html_url":"https://github.com/caspervdw","followers_url":"https://api.github.com/users/caspervdw/followers","following_url":"https://api.github.com/users/caspervdw/following{/other_user}","gists_url":"https://api.github.com/users/caspervdw/gists{/gist_id}","starred_url":"https://api.github.com/users/caspervdw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caspervdw/subscriptions","organizations_url":"https://api.github.com/users/caspervdw/orgs","repos_url":"https://api.github.com/users/caspervdw/repos","events_url":"https://api.github.com/users/caspervdw/events{/privacy}","received_events_url":"https://api.github.com/users/caspervdw/received_events","type":"User","site_admin":false},"body":"Right, I basically implemented this to get IPython mulciprocessing to work (which, by the way, isn't tested yet, see issue on trackpy). I thought that a file reference in general is not picklable. A seperate issue could definitely improve on this.","created_at":"2015-09-30T15:10:46Z","updated_at":"2015-09-30T15:10:46Z","html_url":"https://github.com/soft-matter/slicerator/pull/5#discussion_r40805996","pull_request_url":"https://api.github.com/repos/soft-matter/slicerator/pulls/5","_links":{"self":{"href":"https://api.github.com/repos/soft-matter/slicerator/pulls/comments/40805996"},"html":{"href":"https://github.com/soft-matter/slicerator/pull/5#discussion_r40805996"},"pull_request":{"href":"https://api.github.com/repos/soft-matter/slicerator/pulls/5"}}},"pull_request":{"url":"https://api.github.com/repos/soft-matter/slicerator/pulls/5","id":45494209,"html_url":"https://github.com/soft-matter/slicerator/pull/5","diff_url":"https://github.com/soft-matter/slicerator/pull/5.diff","patch_url":"https://github.com/soft-matter/slicerator/pull/5.patch","issue_url":"https://api.github.com/repos/soft-matter/slicerator/issues/5","number":5,"state":"open","locked":false,"title":"ENH/API Construction with decorator & attribute propagation","user":{"login":"caspervdw","id":8937919,"avatar_url":"https://avatars.githubusercontent.com/u/8937919?v=3","gravatar_id":"","url":"https://api.github.com/users/caspervdw","html_url":"https://github.com/caspervdw","followers_url":"https://api.github.com/users/caspervdw/followers","following_url":"https://api.github.com/users/caspervdw/following{/other_user}","gists_url":"https://api.github.com/users/caspervdw/gists{/gist_id}","starred_url":"https://api.github.com/users/caspervdw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caspervdw/subscriptions","organizations_url":"https://api.github.com/users/caspervdw/orgs","repos_url":"https://api.github.com/users/caspervdw/repos","events_url":"https://api.github.com/users/caspervdw/events{/privacy}","received_events_url":"https://api.github.com/users/caspervdw/received_events","type":"User","site_admin":false},"body":"This continues the work of @danielballan in https://github.com/soft-matter/slicerator/pull/4, it adds a decorator to easily implement `Slicerator` in classes.\r\n\r\nAlso it fixes some issues with attribute propagation. It doesn't work with `__getattr__` anymore; propagated attributes are actually set to the Slicerator with `setattr` at construction. Attributes that are indexed (`slice_attrs`) will be wrapped applying `_map_index` to the index.\r\n\r\n    class Dummy(object):\r\n        def __init__(self):\r\n            self.frame = list('abcdefghij')\r\n            self._time = list(range(len(self.frame)))\r\n            self._filename = 'filename'\r\n\r\n        def __len__(self):\r\n            return len(self.frame)\r\n\r\n        @slicerate(slice_attrs=['time'], expose_attrs=['filename'])\r\n        def __getitem__(self, i):\r\n            return self.frame[i]  # actual code of get_frame\r\n\r\n        def time(self, i):\r\n            return self._time[i]\r\n\r\n        @property\r\n        def filename(self):\r\n            return self._filename","created_at":"2015-09-20T10:50:06Z","updated_at":"2015-09-30T15:10:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5a0f1be987c407b6075bdad0512987ffa1899a3c","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/soft-matter/slicerator/pulls/5/commits","review_comments_url":"https://api.github.com/repos/soft-matter/slicerator/pulls/5/comments","review_comment_url":"https://api.github.com/repos/soft-matter/slicerator/pulls/comments{/number}","comments_url":"https://api.github.com/repos/soft-matter/slicerator/issues/5/comments","statuses_url":"https://api.github.com/repos/soft-matter/slicerator/statuses/eeef867f7538879a76fec24a992e66e2feb79f39","head":{"label":"caspervdw:slicerate","ref":"slicerate","sha":"eeef867f7538879a76fec24a992e66e2feb79f39","user":{"login":"caspervdw","id":8937919,"avatar_url":"https://avatars.githubusercontent.com/u/8937919?v=3","gravatar_id":"","url":"https://api.github.com/users/caspervdw","html_url":"https://github.com/caspervdw","followers_url":"https://api.github.com/users/caspervdw/followers","following_url":"https://api.github.com/users/caspervdw/following{/other_user}","gists_url":"https://api.github.com/users/caspervdw/gists{/gist_id}","starred_url":"https://api.github.com/users/caspervdw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caspervdw/subscriptions","organizations_url":"https://api.github.com/users/caspervdw/orgs","repos_url":"https://api.github.com/users/caspervdw/repos","events_url":"https://api.github.com/users/caspervdw/events{/privacy}","received_events_url":"https://api.github.com/users/caspervdw/received_events","type":"User","site_admin":false},"repo":{"id":42712646,"name":"slicerator","full_name":"caspervdw/slicerator","owner":{"login":"caspervdw","id":8937919,"avatar_url":"https://avatars.githubusercontent.com/u/8937919?v=3","gravatar_id":"","url":"https://api.github.com/users/caspervdw","html_url":"https://github.com/caspervdw","followers_url":"https://api.github.com/users/caspervdw/followers","following_url":"https://api.github.com/users/caspervdw/following{/other_user}","gists_url":"https://api.github.com/users/caspervdw/gists{/gist_id}","starred_url":"https://api.github.com/users/caspervdw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caspervdw/subscriptions","organizations_url":"https://api.github.com/users/caspervdw/orgs","repos_url":"https://api.github.com/users/caspervdw/repos","events_url":"https://api.github.com/users/caspervdw/events{/privacy}","received_events_url":"https://api.github.com/users/caspervdw/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/caspervdw/slicerator","description":"a lazy-loading, fancy-sliceable iterable","fork":true,"url":"https://api.github.com/repos/caspervdw/slicerator","forks_url":"https://api.github.com/repos/caspervdw/slicerator/forks","keys_url":"https://api.github.com/repos/caspervdw/slicerator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/caspervdw/slicerator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/caspervdw/slicerator/teams","hooks_url":"https://api.github.com/repos/caspervdw/slicerator/hooks","issue_events_url":"https://api.github.com/repos/caspervdw/slicerator/issues/events{/number}","events_url":"https://api.github.com/repos/caspervdw/slicerator/events","assignees_url":"https://api.github.com/repos/caspervdw/slicerator/assignees{/user}","branches_url":"https://api.github.com/repos/caspervdw/slicerator/branches{/branch}","tags_url":"https://api.github.com/repos/caspervdw/slicerator/tags","blobs_url":"https://api.github.com/repos/caspervdw/slicerator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/caspervdw/slicerator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/caspervdw/slicerator/git/refs{/sha}","trees_url":"https://api.github.com/repos/caspervdw/slicerator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/caspervdw/slicerator/statuses/{sha}","languages_url":"https://api.github.com/repos/caspervdw/slicerator/languages","stargazers_url":"https://api.github.com/repos/caspervdw/slicerator/stargazers","contributors_url":"https://api.github.com/repos/caspervdw/slicerator/contributors","subscribers_url":"https://api.github.com/repos/caspervdw/slicerator/subscribers","subscription_url":"https://api.github.com/repos/caspervdw/slicerator/subscription","commits_url":"https://api.github.com/repos/caspervdw/slicerator/commits{/sha}","git_commits_url":"https://api.github.com/repos/caspervdw/slicerator/git/commits{/sha}","comments_url":"https://api.github.com/repos/caspervdw/slicerator/comments{/number}","issue_comment_url":"https://api.github.com/repos/caspervdw/slicerator/issues/comments{/number}","contents_url":"https://api.github.com/repos/caspervdw/slicerator/contents/{+path}","compare_url":"https://api.github.com/repos/caspervdw/slicerator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/caspervdw/slicerator/merges","archive_url":"https://api.github.com/repos/caspervdw/slicerator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/caspervdw/slicerator/downloads","issues_url":"https://api.github.com/repos/caspervdw/slicerator/issues{/number}","pulls_url":"https://api.github.com/repos/caspervdw/slicerator/pulls{/number}","milestones_url":"https://api.github.com/repos/caspervdw/slicerator/milestones{/number}","notifications_url":"https://api.github.com/repos/caspervdw/slicerator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/caspervdw/slicerator/labels{/name}","releases_url":"https://api.github.com/repos/caspervdw/slicerator/releases{/id}","created_at":"2015-09-18T09:35:52Z","updated_at":"2015-09-18T09:35:53Z","pushed_at":"2015-09-27T18:40:31Z","git_url":"git://github.com/caspervdw/slicerator.git","ssh_url":"git@github.com:caspervdw/slicerator.git","clone_url":"https://github.com/caspervdw/slicerator.git","svn_url":"https://github.com/caspervdw/slicerator","homepage":null,"size":165,"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":"soft-matter:master","ref":"master","sha":"b6d1b464f2c148760d3b6d24cf45e790da089100","user":{"login":"soft-matter","id":5857177,"avatar_url":"https://avatars.githubusercontent.com/u/5857177?v=3","gravatar_id":"","url":"https://api.github.com/users/soft-matter","html_url":"https://github.com/soft-matter","followers_url":"https://api.github.com/users/soft-matter/followers","following_url":"https://api.github.com/users/soft-matter/following{/other_user}","gists_url":"https://api.github.com/users/soft-matter/gists{/gist_id}","starred_url":"https://api.github.com/users/soft-matter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/soft-matter/subscriptions","organizations_url":"https://api.github.com/users/soft-matter/orgs","repos_url":"https://api.github.com/users/soft-matter/repos","events_url":"https://api.github.com/users/soft-matter/events{/privacy}","received_events_url":"https://api.github.com/users/soft-matter/received_events","type":"Organization","site_admin":false},"repo":{"id":39966805,"name":"slicerator","full_name":"soft-matter/slicerator","owner":{"login":"soft-matter","id":5857177,"avatar_url":"https://avatars.githubusercontent.com/u/5857177?v=3","gravatar_id":"","url":"https://api.github.com/users/soft-matter","html_url":"https://github.com/soft-matter","followers_url":"https://api.github.com/users/soft-matter/followers","following_url":"https://api.github.com/users/soft-matter/following{/other_user}","gists_url":"https://api.github.com/users/soft-matter/gists{/gist_id}","starred_url":"https://api.github.com/users/soft-matter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/soft-matter/subscriptions","organizations_url":"https://api.github.com/users/soft-matter/orgs","repos_url":"https://api.github.com/users/soft-matter/repos","events_url":"https://api.github.com/users/soft-matter/events{/privacy}","received_events_url":"https://api.github.com/users/soft-matter/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/soft-matter/slicerator","description":"a lazy-loading, fancy-sliceable iterable","fork":false,"url":"https://api.github.com/repos/soft-matter/slicerator","forks_url":"https://api.github.com/repos/soft-matter/slicerator/forks","keys_url":"https://api.github.com/repos/soft-matter/slicerator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/soft-matter/slicerator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/soft-matter/slicerator/teams","hooks_url":"https://api.github.com/repos/soft-matter/slicerator/hooks","issue_events_url":"https://api.github.com/repos/soft-matter/slicerator/issues/events{/number}","events_url":"https://api.github.com/repos/soft-matter/slicerator/events","assignees_url":"https://api.github.com/repos/soft-matter/slicerator/assignees{/user}","branches_url":"https://api.github.com/repos/soft-matter/slicerator/branches{/branch}","tags_url":"https://api.github.com/repos/soft-matter/slicerator/tags","blobs_url":"https://api.github.com/repos/soft-matter/slicerator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/soft-matter/slicerator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/soft-matter/slicerator/git/refs{/sha}","trees_url":"https://api.github.com/repos/soft-matter/slicerator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/soft-matter/slicerator/statuses/{sha}","languages_url":"https://api.github.com/repos/soft-matter/slicerator/languages","stargazers_url":"https://api.github.com/repos/soft-matter/slicerator/stargazers","contributors_url":"https://api.github.com/repos/soft-matter/slicerator/contributors","subscribers_url":"https://api.github.com/repos/soft-matter/slicerator/subscribers","subscription_url":"https://api.github.com/repos/soft-matter/slicerator/subscription","commits_url":"https://api.github.com/repos/soft-matter/slicerator/commits{/sha}","git_commits_url":"https://api.github.com/repos/soft-matter/slicerator/git/commits{/sha}","comments_url":"https://api.github.com/repos/soft-matter/slicerator/comments{/number}","issue_comment_url":"https://api.github.com/repos/soft-matter/slicerator/issues/comments{/number}","contents_url":"https://api.github.com/repos/soft-matter/slicerator/contents/{+path}","compare_url":"https://api.github.com/repos/soft-matter/slicerator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/soft-matter/slicerator/merges","archive_url":"https://api.github.com/repos/soft-matter/slicerator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/soft-matter/slicerator/downloads","issues_url":"https://api.github.com/repos/soft-matter/slicerator/issues{/number}","pulls_url":"https://api.github.com/repos/soft-matter/slicerator/pulls{/number}","milestones_url":"https://api.github.com/repos/soft-matter/slicerator/milestones{/number}","notifications_url":"https://api.github.com/repos/soft-matter/slicerator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/soft-matter/slicerator/labels{/name}","releases_url":"https://api.github.com/repos/soft-matter/slicerator/releases{/id}","created_at":"2015-07-30T18:45:25Z","updated_at":"2015-07-30T18:45:39Z","pushed_at":"2015-09-27T18:40:31Z","git_url":"git://github.com/soft-matter/slicerator.git","ssh_url":"git@github.com:soft-matter/slicerator.git","clone_url":"https://github.com/soft-matter/slicerator.git","svn_url":"https://github.com/soft-matter/slicerator","homepage":null,"size":352,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":3,"forks":2,"open_issues":3,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/soft-matter/slicerator/pulls/5"},"html":{"href":"https://github.com/soft-matter/slicerator/pull/5"},"issue":{"href":"https://api.github.com/repos/soft-matter/slicerator/issues/5"},"comments":{"href":"https://api.github.com/repos/soft-matter/slicerator/issues/5/comments"},"review_comments":{"href":"https://api.github.com/repos/soft-matter/slicerator/pulls/5/comments"},"review_comment":{"href":"https://api.github.com/repos/soft-matter/slicerator/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/soft-matter/slicerator/pulls/5/commits"},"statuses":{"href":"https://api.github.com/repos/soft-matter/slicerator/statuses/eeef867f7538879a76fec24a992e66e2feb79f39"}}}}