{"action":"created","comment":{"url":"https://api.github.com/repos/xonsh/xonsh/pulls/comments/76511727","id":76511727,"diff_hunk":"@@ -0,0 +1,169 @@\n+\"\"\"\n+Events for xonsh.\n+\n+In all likelihood, you want builtins.__xonsh_events__\n+\n+The best way to \"declare\" an event is something like::\n+\n+    __xonsh_events__.on_spam.doc(\"Comes with eggs\")\n+\"\"\"\n+import abc\n+import collections.abc\n+import traceback\n+import sys\n+\n+\n+class AbstractEvent(collections.abc.MutableSet, abc.ABC):\n+    def __call__(self, handler):\n+        \"\"\"\n+        Registers a handler. It's suggested to use this as a decorator.\n+\n+        A decorator method is added to the handler, validator(). If a validator\n+        function is added, it can filter if the handler will be considered. The\n+        validator takes the same arguments as the handler. If it returns False,\n+        the handler will not called or considered, as if it was not registered\n+        at all.\n+        \"\"\"\n+        #  Using Pythons \"private\" munging to minimize hypothetical collisions\n+        handler.__validator = None\n+        self.add(handler)\n+\n+        def validator(vfunc):\n+            \"\"\"\n+            Adds a validator function to a handler to limit when it is considered.\n+            \"\"\"\n+            handler.__validator = vfunc\n+        handler.validator = validator\n+\n+        return handler\n+\n+    def _filterhandlers(self, handlers, *pargs, **kwargs):\n+        \"\"\"\n+        Helper method for implementing classes. Generates the handlers that pass validation.\n+        \"\"\"\n+        for handler in handlers:\n+            if handler.__validator is not None and not handler.__validator(*pargs, **kwargs):\n+                continue\n+            yield handler\n+\n+    @abc.abstractmethod\n+    def fire(self, *pargs, **kwargs):\n+        \"\"\"\n+        Fires an event, calling registered handlers with the given arguments.\n+        \"\"\"\n+\n+\n+class Event(AbstractEvent):\n+    \"\"\"\n+    A given event that handlers can register against.\n+\n+    Acts as a ``set`` for registered handlers.\n+\n+    Note that ordering is never guaranteed.\n+    \"\"\"\n+    # Wish I could just pull from set...\n+    def __init__(self):\n+        self._handlers = set()\n+\n+    def __len__(self):\n+        return len(self._handlers)\n+\n+    def __contains__(self, item):\n+        return item in self._handlers\n+\n+    def __iter__(self):\n+        yield from self._handlers\n+\n+    def add(self, item):\n+        self._handlers.add(item)\n+\n+    def discard(self, item):\n+        self._handlers.discard(item)\n+\n+    def fire(self, *pargs, **kwargs):\n+        \"\"\"\n+        Fires each event, returning a non-unique iterable of the results.\n+        \"\"\"\n+        vals = []\n+        for handler in self._filterhandlers(self._handlers, *pargs, **kwargs):\n+            try:\n+                rv = handler(*pargs, **kwargs)\n+            except Exception:\n+                print(\"Exception raised in event handler; ignored.\", file=sys.stderr)\n+                traceback.print_exc()\n+            else:\n+                vals.append(rv)\n+        return vals\n+\n+\n+class LoadEvent(AbstractEvent):\n+    \"\"\"\n+    A kind of event in which each handler is called exactly once.\n+    \"\"\"\n+    def __init__(self):\n+        self._fired = set()\n+        self._unfired = set()\n+        self._hasfired = False\n+\n+    def __len__(self):\n+        return len(self._fired) + len(self._unfired)\n+\n+    def __contains__(self, item):\n+        return item in self._fired or item in self._unfired\n+\n+    def __iter__(self):\n+        yield from self._fired\n+        yield from self._unfired\n+\n+    def add(self, item):\n+        self._fired.add(item)\n+\n+    def discard(self, item):\n+        self._fired.discard(item)\n+        self._unfired.discard(item)\n+\n+    def fire(self, *pargs, **kwargs):\n+        raise NotImplementedError(\"See #1550\")\n+\n+\n+class EventManager:\n+    \"\"\"\n+    Container for all events in a system.\n+\n+    Meant to be a singleton, but doesn't enforce that itself.\n+\n+    Each event is just an attribute. They're created dynamically on first use.\n+    \"\"\"\n+\n+    def doc(self, name, docstring):\n+        \"\"\"\n+        Applies a docstring to an event.\n+        \"\"\"\n+        type(getattr(self, name)).__doc__ = docstring\n+\n+    def transmogrify(self, name, klass):\n+        \"\"\"\n+        Converts an event from one species to another.\n+\n+        Please note: Some species may do special things with handlers. This is lost.\n+        \"\"\"\n+        if isinstance(klass, str):\n+            klass = globals()[klass]\n+\n+        if not issubclass(klass, AbstractEvent):\n+            raise ValueError(\"Invalid event class; must be a subclass of AbstractEvent\")\n+\n+        oldevent = getattr(self, name)\n+        newevent = type(name, (klass,), {'__doc__': type(oldevent).__doc__})()\n+        setattr(self, name, newevent)\n+\n+        for handler in oldevent:\n+            newevent.add(handler)\n+\n+    def __getattr__(self, name):\n+        e = type(name, (Event,), {'__doc__': None})()\n+        setattr(self, name, e)\n+        return e\n+\n+\n+events = EventManager()","path":"xonsh/events.py","position":null,"original_position":169,"commit_id":"e49c37e67c22c1eea81bbff2e584d1b1fe3306b4","original_commit_id":"78783c71b5d1949a2967f65c3a65d49fed139a34","user":{"login":"astronouth7303","id":401438,"avatar_url":"https://avatars.githubusercontent.com/u/401438?v=3","gravatar_id":"","url":"https://api.github.com/users/astronouth7303","html_url":"https://github.com/astronouth7303","followers_url":"https://api.github.com/users/astronouth7303/followers","following_url":"https://api.github.com/users/astronouth7303/following{/other_user}","gists_url":"https://api.github.com/users/astronouth7303/gists{/gist_id}","starred_url":"https://api.github.com/users/astronouth7303/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astronouth7303/subscriptions","organizations_url":"https://api.github.com/users/astronouth7303/orgs","repos_url":"https://api.github.com/users/astronouth7303/repos","events_url":"https://api.github.com/users/astronouth7303/events{/privacy}","received_events_url":"https://api.github.com/users/astronouth7303/received_events","type":"User","site_admin":false},"body":"Initialization is very nearly the cheapest it possibly can and it's expected to be used at load time. I'm pretty sure in this case using lazyobject would be more expensive because of overhead.\r\n\r\nComment will be in the next commit, though.","created_at":"2016-08-27T06:53:50Z","updated_at":"2016-08-27T06:53:50Z","html_url":"https://github.com/xonsh/xonsh/pull/1501#discussion_r76511727","pull_request_url":"https://api.github.com/repos/xonsh/xonsh/pulls/1501","_links":{"self":{"href":"https://api.github.com/repos/xonsh/xonsh/pulls/comments/76511727"},"html":{"href":"https://github.com/xonsh/xonsh/pull/1501#discussion_r76511727"},"pull_request":{"href":"https://api.github.com/repos/xonsh/xonsh/pulls/1501"}}},"pull_request":{"url":"https://api.github.com/repos/xonsh/xonsh/pulls/1501","id":79137092,"html_url":"https://github.com/xonsh/xonsh/pull/1501","diff_url":"https://github.com/xonsh/xonsh/pull/1501.diff","patch_url":"https://github.com/xonsh/xonsh/pull/1501.patch","issue_url":"https://api.github.com/repos/xonsh/xonsh/issues/1501","number":1501,"state":"open","locked":false,"title":"Implementation of Event Subsystem","user":{"login":"astronouth7303","id":401438,"avatar_url":"https://avatars.githubusercontent.com/u/401438?v=3","gravatar_id":"","url":"https://api.github.com/users/astronouth7303","html_url":"https://github.com/astronouth7303","followers_url":"https://api.github.com/users/astronouth7303/followers","following_url":"https://api.github.com/users/astronouth7303/following{/other_user}","gists_url":"https://api.github.com/users/astronouth7303/gists{/gist_id}","starred_url":"https://api.github.com/users/astronouth7303/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astronouth7303/subscriptions","organizations_url":"https://api.github.com/users/astronouth7303/orgs","repos_url":"https://api.github.com/users/astronouth7303/repos","events_url":"https://api.github.com/users/astronouth7303/events{/privacy}","received_events_url":"https://api.github.com/users/astronouth7303/received_events","type":"User","site_admin":false},"body":"Fixes #1489.\r\n\r\nThis is my stab at an event bus, after talking to @scopatz.\r\n\r\nPlease! Comments, questions, etc! I'm open to any ideas, or even scrapping this entire model of events.\r\n\r\nBasic usage, as it stands:\r\n\r\n```python\r\n@events.on_precommand\r\ndef myhandler(cmd):\r\n    print(\"About to run command:\", cmd)\r\n```\r\n\r\nThis covers stand-alone functions, static methods, and maybe class methods? Instance methods certainly do not work.\r\n\r\nI believe the docstrings cover most of the usage, even if they currently ignore rst and numpy.","created_at":"2016-07-28T04:53:07Z","updated_at":"2016-08-27T06:53:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8a32b40cd2250b5e36d99657248ec4e2780b5549","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/xonsh/xonsh/pulls/1501/commits","review_comments_url":"https://api.github.com/repos/xonsh/xonsh/pulls/1501/comments","review_comment_url":"https://api.github.com/repos/xonsh/xonsh/pulls/comments{/number}","comments_url":"https://api.github.com/repos/xonsh/xonsh/issues/1501/comments","statuses_url":"https://api.github.com/repos/xonsh/xonsh/statuses/e49c37e67c22c1eea81bbff2e584d1b1fe3306b4","head":{"label":"astronouth7303:events","ref":"events","sha":"e49c37e67c22c1eea81bbff2e584d1b1fe3306b4","user":{"login":"astronouth7303","id":401438,"avatar_url":"https://avatars.githubusercontent.com/u/401438?v=3","gravatar_id":"","url":"https://api.github.com/users/astronouth7303","html_url":"https://github.com/astronouth7303","followers_url":"https://api.github.com/users/astronouth7303/followers","following_url":"https://api.github.com/users/astronouth7303/following{/other_user}","gists_url":"https://api.github.com/users/astronouth7303/gists{/gist_id}","starred_url":"https://api.github.com/users/astronouth7303/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astronouth7303/subscriptions","organizations_url":"https://api.github.com/users/astronouth7303/orgs","repos_url":"https://api.github.com/users/astronouth7303/repos","events_url":"https://api.github.com/users/astronouth7303/events{/privacy}","received_events_url":"https://api.github.com/users/astronouth7303/received_events","type":"User","site_admin":false},"repo":{"id":63803011,"name":"xonsh","full_name":"astronouth7303/xonsh","owner":{"login":"astronouth7303","id":401438,"avatar_url":"https://avatars.githubusercontent.com/u/401438?v=3","gravatar_id":"","url":"https://api.github.com/users/astronouth7303","html_url":"https://github.com/astronouth7303","followers_url":"https://api.github.com/users/astronouth7303/followers","following_url":"https://api.github.com/users/astronouth7303/following{/other_user}","gists_url":"https://api.github.com/users/astronouth7303/gists{/gist_id}","starred_url":"https://api.github.com/users/astronouth7303/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/astronouth7303/subscriptions","organizations_url":"https://api.github.com/users/astronouth7303/orgs","repos_url":"https://api.github.com/users/astronouth7303/repos","events_url":"https://api.github.com/users/astronouth7303/events{/privacy}","received_events_url":"https://api.github.com/users/astronouth7303/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/astronouth7303/xonsh","description":"xonsh is a Python-ish, BASHwards-facing shell.","fork":true,"url":"https://api.github.com/repos/astronouth7303/xonsh","forks_url":"https://api.github.com/repos/astronouth7303/xonsh/forks","keys_url":"https://api.github.com/repos/astronouth7303/xonsh/keys{/key_id}","collaborators_url":"https://api.github.com/repos/astronouth7303/xonsh/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/astronouth7303/xonsh/teams","hooks_url":"https://api.github.com/repos/astronouth7303/xonsh/hooks","issue_events_url":"https://api.github.com/repos/astronouth7303/xonsh/issues/events{/number}","events_url":"https://api.github.com/repos/astronouth7303/xonsh/events","assignees_url":"https://api.github.com/repos/astronouth7303/xonsh/assignees{/user}","branches_url":"https://api.github.com/repos/astronouth7303/xonsh/branches{/branch}","tags_url":"https://api.github.com/repos/astronouth7303/xonsh/tags","blobs_url":"https://api.github.com/repos/astronouth7303/xonsh/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/astronouth7303/xonsh/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/astronouth7303/xonsh/git/refs{/sha}","trees_url":"https://api.github.com/repos/astronouth7303/xonsh/git/trees{/sha}","statuses_url":"https://api.github.com/repos/astronouth7303/xonsh/statuses/{sha}","languages_url":"https://api.github.com/repos/astronouth7303/xonsh/languages","stargazers_url":"https://api.github.com/repos/astronouth7303/xonsh/stargazers","contributors_url":"https://api.github.com/repos/astronouth7303/xonsh/contributors","subscribers_url":"https://api.github.com/repos/astronouth7303/xonsh/subscribers","subscription_url":"https://api.github.com/repos/astronouth7303/xonsh/subscription","commits_url":"https://api.github.com/repos/astronouth7303/xonsh/commits{/sha}","git_commits_url":"https://api.github.com/repos/astronouth7303/xonsh/git/commits{/sha}","comments_url":"https://api.github.com/repos/astronouth7303/xonsh/comments{/number}","issue_comment_url":"https://api.github.com/repos/astronouth7303/xonsh/issues/comments{/number}","contents_url":"https://api.github.com/repos/astronouth7303/xonsh/contents/{+path}","compare_url":"https://api.github.com/repos/astronouth7303/xonsh/compare/{base}...{head}","merges_url":"https://api.github.com/repos/astronouth7303/xonsh/merges","archive_url":"https://api.github.com/repos/astronouth7303/xonsh/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/astronouth7303/xonsh/downloads","issues_url":"https://api.github.com/repos/astronouth7303/xonsh/issues{/number}","pulls_url":"https://api.github.com/repos/astronouth7303/xonsh/pulls{/number}","milestones_url":"https://api.github.com/repos/astronouth7303/xonsh/milestones{/number}","notifications_url":"https://api.github.com/repos/astronouth7303/xonsh/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/astronouth7303/xonsh/labels{/name}","releases_url":"https://api.github.com/repos/astronouth7303/xonsh/releases{/id}","deployments_url":"https://api.github.com/repos/astronouth7303/xonsh/deployments","created_at":"2016-07-20T18:00:58Z","updated_at":"2016-07-20T18:00:59Z","pushed_at":"2016-08-26T21:46:51Z","git_url":"git://github.com/astronouth7303/xonsh.git","ssh_url":"git@github.com:astronouth7303/xonsh.git","clone_url":"https://github.com/astronouth7303/xonsh.git","svn_url":"https://github.com/astronouth7303/xonsh","homepage":"http://xon.sh","size":6977,"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":"xonsh:master","ref":"master","sha":"9b279ee53fb28d057f9a1c526379eef2fb75751a","user":{"login":"xonsh","id":17418188,"avatar_url":"https://avatars.githubusercontent.com/u/17418188?v=3","gravatar_id":"","url":"https://api.github.com/users/xonsh","html_url":"https://github.com/xonsh","followers_url":"https://api.github.com/users/xonsh/followers","following_url":"https://api.github.com/users/xonsh/following{/other_user}","gists_url":"https://api.github.com/users/xonsh/gists{/gist_id}","starred_url":"https://api.github.com/users/xonsh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xonsh/subscriptions","organizations_url":"https://api.github.com/users/xonsh/orgs","repos_url":"https://api.github.com/users/xonsh/repos","events_url":"https://api.github.com/users/xonsh/events{/privacy}","received_events_url":"https://api.github.com/users/xonsh/received_events","type":"Organization","site_admin":false},"repo":{"id":29620400,"name":"xonsh","full_name":"xonsh/xonsh","owner":{"login":"xonsh","id":17418188,"avatar_url":"https://avatars.githubusercontent.com/u/17418188?v=3","gravatar_id":"","url":"https://api.github.com/users/xonsh","html_url":"https://github.com/xonsh","followers_url":"https://api.github.com/users/xonsh/followers","following_url":"https://api.github.com/users/xonsh/following{/other_user}","gists_url":"https://api.github.com/users/xonsh/gists{/gist_id}","starred_url":"https://api.github.com/users/xonsh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xonsh/subscriptions","organizations_url":"https://api.github.com/users/xonsh/orgs","repos_url":"https://api.github.com/users/xonsh/repos","events_url":"https://api.github.com/users/xonsh/events{/privacy}","received_events_url":"https://api.github.com/users/xonsh/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/xonsh/xonsh","description":"xonsh is a Python-ish, BASHwards-facing shell.","fork":false,"url":"https://api.github.com/repos/xonsh/xonsh","forks_url":"https://api.github.com/repos/xonsh/xonsh/forks","keys_url":"https://api.github.com/repos/xonsh/xonsh/keys{/key_id}","collaborators_url":"https://api.github.com/repos/xonsh/xonsh/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/xonsh/xonsh/teams","hooks_url":"https://api.github.com/repos/xonsh/xonsh/hooks","issue_events_url":"https://api.github.com/repos/xonsh/xonsh/issues/events{/number}","events_url":"https://api.github.com/repos/xonsh/xonsh/events","assignees_url":"https://api.github.com/repos/xonsh/xonsh/assignees{/user}","branches_url":"https://api.github.com/repos/xonsh/xonsh/branches{/branch}","tags_url":"https://api.github.com/repos/xonsh/xonsh/tags","blobs_url":"https://api.github.com/repos/xonsh/xonsh/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/xonsh/xonsh/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/xonsh/xonsh/git/refs{/sha}","trees_url":"https://api.github.com/repos/xonsh/xonsh/git/trees{/sha}","statuses_url":"https://api.github.com/repos/xonsh/xonsh/statuses/{sha}","languages_url":"https://api.github.com/repos/xonsh/xonsh/languages","stargazers_url":"https://api.github.com/repos/xonsh/xonsh/stargazers","contributors_url":"https://api.github.com/repos/xonsh/xonsh/contributors","subscribers_url":"https://api.github.com/repos/xonsh/xonsh/subscribers","subscription_url":"https://api.github.com/repos/xonsh/xonsh/subscription","commits_url":"https://api.github.com/repos/xonsh/xonsh/commits{/sha}","git_commits_url":"https://api.github.com/repos/xonsh/xonsh/git/commits{/sha}","comments_url":"https://api.github.com/repos/xonsh/xonsh/comments{/number}","issue_comment_url":"https://api.github.com/repos/xonsh/xonsh/issues/comments{/number}","contents_url":"https://api.github.com/repos/xonsh/xonsh/contents/{+path}","compare_url":"https://api.github.com/repos/xonsh/xonsh/compare/{base}...{head}","merges_url":"https://api.github.com/repos/xonsh/xonsh/merges","archive_url":"https://api.github.com/repos/xonsh/xonsh/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/xonsh/xonsh/downloads","issues_url":"https://api.github.com/repos/xonsh/xonsh/issues{/number}","pulls_url":"https://api.github.com/repos/xonsh/xonsh/pulls{/number}","milestones_url":"https://api.github.com/repos/xonsh/xonsh/milestones{/number}","notifications_url":"https://api.github.com/repos/xonsh/xonsh/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/xonsh/xonsh/labels{/name}","releases_url":"https://api.github.com/repos/xonsh/xonsh/releases{/id}","deployments_url":"https://api.github.com/repos/xonsh/xonsh/deployments","created_at":"2015-01-21T22:05:27Z","updated_at":"2016-08-27T06:51:52Z","pushed_at":"2016-08-27T01:30:24Z","git_url":"git://github.com/xonsh/xonsh.git","ssh_url":"git@github.com:xonsh/xonsh.git","clone_url":"https://github.com/xonsh/xonsh.git","svn_url":"https://github.com/xonsh/xonsh","homepage":"http://xon.sh","size":8136,"stargazers_count":1662,"watchers_count":1662,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":176,"mirror_url":null,"open_issues_count":246,"forks":176,"open_issues":246,"watchers":1662,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/xonsh/xonsh/pulls/1501"},"html":{"href":"https://github.com/xonsh/xonsh/pull/1501"},"issue":{"href":"https://api.github.com/repos/xonsh/xonsh/issues/1501"},"comments":{"href":"https://api.github.com/repos/xonsh/xonsh/issues/1501/comments"},"review_comments":{"href":"https://api.github.com/repos/xonsh/xonsh/pulls/1501/comments"},"review_comment":{"href":"https://api.github.com/repos/xonsh/xonsh/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/xonsh/xonsh/pulls/1501/commits"},"statuses":{"href":"https://api.github.com/repos/xonsh/xonsh/statuses/e49c37e67c22c1eea81bbff2e584d1b1fe3306b4"}}}}
{"actor":{"display_login":"astronouth7303"}}