mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-06 09:20:57 +01:00
commit
4afd9da1f0
8 changed files with 219 additions and 37 deletions
31
Makefile
Normal file
31
Makefile
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Make GNU Make xonshy
|
||||
SHELL=xonsh
|
||||
.SHELLFLAGS=-c
|
||||
.ONESHELL:
|
||||
.SILENT:
|
||||
|
||||
# Unlike normal makefiles: executes the entire body in one go under xonsh, and doesn't echo
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
print("""
|
||||
Utility file for xonsh project. Try these targets:
|
||||
* amalgamate: Generate __amalgam__.py files
|
||||
* clean: Remove generated files (namely, the amalgamations)
|
||||
* xonsh/ply: Pull down most recent ply
|
||||
""")
|
||||
|
||||
xonsh/ply:
|
||||
git subtree pull --prefix xonsh/ply https://github.com/dabeaz/ply.git master --squash
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
find xonsh -name __amalgam__.py -delete -print
|
||||
|
||||
.PHONY: amalgamate
|
||||
amalgamate:
|
||||
sys.path.insert(0, '.')
|
||||
import setup
|
||||
setup.amalgamate_source()
|
||||
_ = sys.path.pop(0)
|
14
news/load-events.rst
Normal file
14
news/load-events.rst
Normal file
|
@ -0,0 +1,14 @@
|
|||
**Added:**
|
||||
|
||||
* Load events are now available
|
||||
* New events added: ``on_post_init``, ``on_pre_cmdloop``, ``on_pre_rc``, ``on_post_rc``, ``on_ptk_create``
|
||||
|
||||
**Changed:** None
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:** None
|
||||
|
||||
**Security:** None
|
|
@ -3,10 +3,12 @@ import inspect
|
|||
import pytest
|
||||
from xonsh.events import EventManager, Event, LoadEvent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def events():
|
||||
return EventManager()
|
||||
|
||||
|
||||
def test_event_calling(events):
|
||||
called = False
|
||||
|
||||
|
@ -19,6 +21,7 @@ def test_event_calling(events):
|
|||
|
||||
assert called == "eggs"
|
||||
|
||||
|
||||
def test_event_returns(events):
|
||||
called = 0
|
||||
|
||||
|
@ -39,6 +42,7 @@ def test_event_returns(events):
|
|||
assert called == 2
|
||||
assert set(vals) == {1, 2}
|
||||
|
||||
|
||||
def test_validator(events):
|
||||
called = None
|
||||
|
||||
|
@ -92,6 +96,7 @@ def test_transmogrify(events):
|
|||
assert len(events.on_test) == 1
|
||||
assert inspect.getdoc(events.on_test) == docstring
|
||||
|
||||
|
||||
def test_transmogrify_by_string(events):
|
||||
docstring = "Test event"
|
||||
events.doc('on_test', docstring)
|
||||
|
@ -110,6 +115,46 @@ def test_transmogrify_by_string(events):
|
|||
assert len(events.on_test) == 1
|
||||
assert inspect.getdoc(events.on_test) == docstring
|
||||
|
||||
|
||||
def test_load(events):
|
||||
events.transmogrify('on_test', 'LoadEvent')
|
||||
called = 0
|
||||
|
||||
@events.on_test
|
||||
def on_test():
|
||||
nonlocal called
|
||||
called += 1
|
||||
|
||||
assert called == 0
|
||||
|
||||
events.on_test.fire()
|
||||
assert called == 1
|
||||
|
||||
@events.on_test
|
||||
def second():
|
||||
nonlocal called
|
||||
called += 1
|
||||
|
||||
assert called == 2
|
||||
|
||||
def test_load_2nd_call(events):
|
||||
events.transmogrify('on_test', 'LoadEvent')
|
||||
called = 0
|
||||
|
||||
@events.on_test
|
||||
def on_test():
|
||||
nonlocal called
|
||||
called += 1
|
||||
|
||||
assert called == 0
|
||||
|
||||
events.on_test.fire()
|
||||
assert called == 1
|
||||
|
||||
events.on_test.fire()
|
||||
assert called == 1
|
||||
|
||||
|
||||
def test_typos(xonsh_builtins):
|
||||
for name, ev in vars(xonsh_builtins.events).items():
|
||||
if 'pytest' in name:
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env xonsh
|
||||
|
||||
# update the ply repo bundled with xonsh
|
||||
git subtree pull --prefix xonsh/ply https://github.com/dabeaz/ply.git master --squash
|
|
@ -144,7 +144,13 @@ class Event(AbstractEvent):
|
|||
class LoadEvent(AbstractEvent):
|
||||
"""
|
||||
An event species where each handler is called exactly once, shortly after either the event is
|
||||
fired or the handler is registered (whichever is later).
|
||||
fired or the handler is registered (whichever is later). Additional firings are ignored.
|
||||
|
||||
Note: Does not support scatter/gather, due to never knowing when we have all the handlers.
|
||||
|
||||
Note: Maintains a strong reference to pargs/kwargs in case of the addition of future handlers.
|
||||
|
||||
Note: This is currently NOT thread safe.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._fired = set()
|
||||
|
@ -167,7 +173,11 @@ class LoadEvent(AbstractEvent):
|
|||
|
||||
This has no effect if the element is already present.
|
||||
"""
|
||||
if self._hasfired:
|
||||
self._call(item)
|
||||
self._fired.add(item)
|
||||
else:
|
||||
self._unfired.add(item)
|
||||
|
||||
def discard(self, item):
|
||||
"""
|
||||
|
@ -178,8 +188,22 @@ class LoadEvent(AbstractEvent):
|
|||
self._fired.discard(item)
|
||||
self._unfired.discard(item)
|
||||
|
||||
def _call(self, handler):
|
||||
try:
|
||||
handler(*self._pargs, **self._kwargs)
|
||||
except Exception:
|
||||
print_exception("Exception raised in event handler; ignored.")
|
||||
|
||||
def fire(self, *pargs, **kwargs):
|
||||
raise NotImplementedError("See #1550")
|
||||
if self._hasfired:
|
||||
return
|
||||
self._pargs = pargs
|
||||
self._kwargs = kwargs
|
||||
while self._unfired:
|
||||
handler = self._unfired.pop()
|
||||
self._call(handler)
|
||||
self._hasfired = True
|
||||
return () # Entirely for API compatibility
|
||||
|
||||
|
||||
class EventManager:
|
||||
|
|
|
@ -19,6 +19,57 @@ from xonsh.codecache import run_script_with_cache, run_code_with_cache
|
|||
from xonsh.xonfig import xonfig_main
|
||||
from xonsh.lazyimps import pygments, pyghooks
|
||||
from xonsh.imphooks import install_hook
|
||||
from xonsh.events import events
|
||||
|
||||
|
||||
events.transmogrify('on_post_init', 'LoadEvent')
|
||||
events.doc('on_post_init', """
|
||||
on_post_init() -> None
|
||||
|
||||
Fired after all initialization is finished and we're ready to do work.
|
||||
|
||||
NOTE: This is fired before the wizard is automatically started.
|
||||
""")
|
||||
|
||||
events.transmogrify('on_exit', 'LoadEvent')
|
||||
events.doc('on_exit', """
|
||||
on_exit() -> None
|
||||
|
||||
Fired after all commands have been executed, before tear-down occurs.
|
||||
|
||||
NOTE: All the caveats of the atexit module also apply to this event.
|
||||
""")
|
||||
|
||||
|
||||
events.transmogrify('on_pre_cmdloop', 'LoadEvent')
|
||||
events.doc('on_pre_cmdloop', """
|
||||
on_pre_cmdloop() -> None
|
||||
|
||||
Fired just before the command loop is started, if it is.
|
||||
""")
|
||||
|
||||
events.transmogrify('on_post_cmdloop', 'LoadEvent')
|
||||
events.doc('on_post_cmdloop', """
|
||||
on_post_cmdloop() -> None
|
||||
|
||||
Fired just after the command loop finishes, if it is.
|
||||
|
||||
NOTE: All the caveats of the atexit module also apply to this event.
|
||||
""")
|
||||
|
||||
events.transmogrify('on_pre_rc', 'LoadEvent')
|
||||
events.doc('on_pre_rc', """
|
||||
on_pre_rc() -> None
|
||||
|
||||
Fired just before rc files are loaded, if they are.
|
||||
""")
|
||||
|
||||
events.transmogrify('on_post_rc', 'LoadEvent')
|
||||
events.doc('on_post_rc', """
|
||||
on_post_rc() -> None
|
||||
|
||||
Fired just before rc files are loaded, if they are.
|
||||
""")
|
||||
|
||||
|
||||
def get_setproctitle():
|
||||
|
@ -206,8 +257,10 @@ def main(argv=None):
|
|||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
args = premain(argv)
|
||||
events.on_post_init.fire()
|
||||
env = builtins.__xonsh_env__
|
||||
shell = builtins.__xonsh_shell__
|
||||
try:
|
||||
if args.mode == XonshMode.interactive:
|
||||
# enter the shell
|
||||
env['XONSH_INTERACTIVE'] = True
|
||||
|
@ -218,7 +271,11 @@ def main(argv=None):
|
|||
print('Could not find xonsh configuration or run control files.',
|
||||
file=sys.stderr)
|
||||
xonfig_main(['wizard', '--confirm'])
|
||||
events.on_pre_cmdloop.fire()
|
||||
try:
|
||||
shell.shell.cmdloop()
|
||||
finally:
|
||||
events.on_post_cmdloop.fire()
|
||||
elif args.mode == XonshMode.single_command:
|
||||
# run a single command and exit
|
||||
run_code_with_cache(args.command.lstrip(), shell.execer, mode='single')
|
||||
|
@ -238,6 +295,8 @@ def main(argv=None):
|
|||
code = sys.stdin.read()
|
||||
run_code_with_cache(code, shell.execer, glb=shell.ctx, loc=None,
|
||||
mode='exec')
|
||||
finally:
|
||||
events.on_exit.fire()
|
||||
postmain(args)
|
||||
|
||||
|
||||
|
|
|
@ -20,6 +20,15 @@ from xonsh.ptk.completer import PromptToolkitCompleter
|
|||
from xonsh.ptk.history import PromptToolkitHistory
|
||||
from xonsh.ptk.key_bindings import load_xonsh_bindings
|
||||
from xonsh.ptk.shortcuts import Prompter
|
||||
from xonsh.events import events
|
||||
|
||||
|
||||
events.transmogrify('on_ptk_create', 'LoadEvent')
|
||||
events.doc('on_ptk_create', """
|
||||
on_ptk_create(prompter: Prompter, history: PromptToolkitHistory, completer: PromptToolkitCompleter, bindings: KeyBindingManager) ->
|
||||
|
||||
Fired after prompt toolkit has been initialized
|
||||
""")
|
||||
|
||||
|
||||
class PromptToolkitShell(BaseShell):
|
||||
|
@ -37,6 +46,8 @@ class PromptToolkitShell(BaseShell):
|
|||
}
|
||||
self.key_bindings_manager = KeyBindingManager(**key_bindings_manager_args)
|
||||
load_xonsh_bindings(self.key_bindings_manager)
|
||||
# This assumes that PromptToolkitShell is a singleton
|
||||
events.on_ptk_create.fire(self.prompter, self.history, self.pt_completer, self.key_bindings_manager)
|
||||
|
||||
def singleline(self, store_in_history=True, auto_suggest=None,
|
||||
enable_history_search=True, multiline=True, **kwargs):
|
||||
|
|
|
@ -148,5 +148,7 @@ class Shell(object):
|
|||
# load run control files
|
||||
env = builtins.__xonsh_env__
|
||||
rc = env.get('XONSHRC') if rc is None else rc
|
||||
events.on_pre_rc.fire()
|
||||
self.ctx.update(xonshrc_context(rcfiles=rc, execer=self.execer, initial=self.ctx))
|
||||
events.on_post_rc.fire()
|
||||
self.ctx['__name__'] = '__main__'
|
||||
|
|
Loading…
Add table
Reference in a new issue