make progress on rlcompleter, still broken
This commit is contained in:
		
							parent
							
								
									1b5a0289a8
								
							
						
					
					
						commit
						07a49bfaca
					
				|  | @ -232,7 +232,7 @@ helper to do tab completion of wormhole codes: | |||
| ```python | ||||
| from wormhole import create, rlcompleter_helper | ||||
| w = create(appid, relay_url, reactor) | ||||
| rlcompleter_helper("Wormhole code:", w.input_code()) | ||||
| rlcompleter_helper("Wormhole code:", w.input_code(), reactor) | ||||
| d = w.when_code() | ||||
| ``` | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ from attr.validators import provides | |||
| from automat import MethodicalMachine | ||||
| from . import _interfaces | ||||
| 
 | ||||
| def first(outputs): | ||||
|     return list(outputs)[0] | ||||
| 
 | ||||
| @attrs | ||||
| @implementer(_interfaces.ICode) | ||||
| class Code(object): | ||||
|  | @ -79,7 +82,7 @@ class Code(object): | |||
| 
 | ||||
|     S0_idle.upon(set_code, enter=S4_known, outputs=[do_set_code]) | ||||
|     S0_idle.upon(input_code, enter=S1_inputting_nameplate, | ||||
|                  outputs=[do_start_input]) | ||||
|                  outputs=[do_start_input], collector=first) | ||||
|     S1_inputting_nameplate.upon(got_nameplate, enter=S2_inputting_words, | ||||
|                                 outputs=[do_middle_input]) | ||||
|     S2_inputting_words.upon(finished_input, enter=S4_known, | ||||
|  |  | |||
|  | @ -1,11 +1,12 @@ | |||
| from __future__ import print_function, unicode_literals | ||||
| import sys | ||||
| import six | ||||
| from attr import attrs, attrib | ||||
| from twisted.internet.defer import inlineCallbacks, returnValue | ||||
| from twisted.internet.threads import deferToThread, blockingCallFromThread | ||||
| 
 | ||||
| @attrs | ||||
| class CodeInputter: | ||||
| class CodeInputter(object): | ||||
|     _input_helper = attrib() | ||||
|     _reactor = attrib() | ||||
|     def __attrs_post_init__(self): | ||||
|  | @ -23,10 +24,14 @@ class CodeInputter: | |||
|             # completer exceptions are normally silently discarded, which | ||||
|             # makes debugging challenging | ||||
|             print("completer exception: %s" % e) | ||||
|             import traceback | ||||
|             traceback.print_exc() | ||||
|             raise e | ||||
| 
 | ||||
|     def completer(self, text, state): | ||||
|         self.used_completion = True | ||||
|         # debug | ||||
|         #import readline | ||||
|         #if state == 0: | ||||
|         #    print("", file=sys.stderr) | ||||
|         #print("completer: '%s' %d '%d'" % (text, state, | ||||
|  | @ -68,10 +73,10 @@ class CodeInputter: | |||
|         #sys.stderr.flush() | ||||
|         return self._matches[state] | ||||
| 
 | ||||
| def input_code_with_completion(prompt, input_helper, code_length): | ||||
| def input_code_with_completion(prompt, input_helper, reactor): | ||||
|     try: | ||||
|         import readline | ||||
|         c = CodeInputter(input_helper) | ||||
|         c = CodeInputter(input_helper, reactor) | ||||
|         if readline.__doc__ and "libedit" in readline.__doc__: | ||||
|             readline.parse_and_bind("bind ^I rl_complete") | ||||
|         else: | ||||
|  | @ -89,12 +94,45 @@ def input_code_with_completion(prompt, input_helper, code_length): | |||
|     used_completion = c.used_completion if c else False | ||||
|     return (code, used_completion) | ||||
| 
 | ||||
| def warn_readline(): | ||||
|     # When our process receives a SIGINT, Twisted's SIGINT handler will | ||||
|     # stop the reactor and wait for all threads to terminate before the | ||||
|     # process exits. However, if we were waiting for | ||||
|     # input_code_with_completion() when SIGINT happened, the readline | ||||
|     # thread will be blocked waiting for something on stdin. Trick the | ||||
|     # user into satisfying the blocking read so we can exit. | ||||
|     print("\nCommand interrupted: please press Return to quit", | ||||
|           file=sys.stderr) | ||||
| 
 | ||||
|     # Other potential approaches to this problem: | ||||
|     # * hard-terminate our process with os._exit(1), but make sure the | ||||
|     #   tty gets reset to a normal mode ("cooked"?) first, so that the | ||||
|     #   next shell command the user types is echoed correctly | ||||
|     # * track down the thread (t.p.threadable.getThreadID from inside the | ||||
|     #   thread), get a cffi binding to pthread_kill, deliver SIGINT to it | ||||
|     # * allocate a pty pair (pty.openpty), replace sys.stdin with the | ||||
|     #   slave, build a pty bridge that copies bytes (and other PTY | ||||
|     #   things) from the real stdin to the master, then close the slave | ||||
|     #   at shutdown, so readline sees EOF | ||||
|     # * write tab-completion and basic editing (TTY raw mode, | ||||
|     #   backspace-is-erase) without readline, probably with curses or | ||||
|     #   twisted.conch.insults | ||||
|     # * write a separate program to get codes (maybe just "wormhole | ||||
|     #   --internal-get-code"), run it as a subprocess, let it inherit | ||||
|     #   stdin/stdout, send it SIGINT when we receive SIGINT ourselves. It | ||||
|     #   needs an RPC mechanism (over some extra file descriptors) to ask | ||||
|     #   us to fetch the current nameplate_id list. | ||||
|     # | ||||
|     # Note that hard-terminating our process with os.kill(os.getpid(), | ||||
|     # signal.SIGKILL), or SIGTERM, doesn't seem to work: the thread | ||||
|     # doesn't see the signal, and we must still wait for stdin to make | ||||
|     # readline finish. | ||||
| 
 | ||||
| @inlineCallbacks | ||||
| def rlcompleter_helper(prompt, input_helper, reactor): | ||||
|     def warn_readline(): | ||||
|         pass | ||||
|     t = reactor.addSystemEventTrigger("before", "shutdown", warn_readline) | ||||
|     res = yield deferToThread(input_code_with_completion, prompt, input_helper) | ||||
|     res = yield deferToThread(input_code_with_completion, prompt, input_helper, | ||||
|                               reactor) | ||||
|     (code, used_completion) = res | ||||
|     reactor.removeSystemEventTrigger(t) | ||||
|     returnValue(used_completion) | ||||
|  |  | |||
|  | @ -164,9 +164,13 @@ class TwistedReceiver: | |||
|         if code: | ||||
|             w.set_code(code) | ||||
|         else: | ||||
|             raise NotImplemented | ||||
|             yield w.input_code("Enter receive wormhole code: ", # TODO | ||||
|                                self.args.code_length) | ||||
|             from .._rlcompleter import rlcompleter_helper | ||||
|             used_completion = yield rlcompleter_helper("Enter receive wormhole code: ", | ||||
|                                                        w.input_code(), | ||||
|                                                        self._reactor) | ||||
|             if not used_completion: | ||||
|                 print(" (note: you can use <Tab> to complete words)", | ||||
|                       file=self.args.stderr) | ||||
|         yield w.when_code() | ||||
| 
 | ||||
|     def _show_verifier(self, verifier): | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user