diff --git a/setup.py b/setup.py index bb4b6e3..ac4b072 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ setup(name="wormhole-sync", "wormhole.test", "wormhole.util"], entry_points={"console_scripts": ["wormhole = wormhole.scripts.runner:entry"]}, - install_requires=["spake2", "pynacl", "requests", "twisted"], + install_requires=["spake2", "pynacl", "requests", "argparse"], test_suite="wormhole.test", cmdclass=commands, ) diff --git a/src/wormhole/scripts/cmd_receive_file.py b/src/wormhole/scripts/cmd_receive_file.py index dcd677b..f5d4a3c 100644 --- a/src/wormhole/scripts/cmd_receive_file.py +++ b/src/wormhole/scripts/cmd_receive_file.py @@ -6,9 +6,9 @@ from .progress import start_progress, update_progress, finish_progress APPID = "lothar.com/wormhole/file-xfer" -def receive_file(so): +def receive_file(args): # we're receiving - transit_receiver = TransitReceiver(transit_relay=so.parent["transit-helper"]) + transit_receiver = TransitReceiver(transit_relay=args.transit_helper) mydata = json.dumps({ "transit": { @@ -16,8 +16,8 @@ def receive_file(so): "relay_connection_hints": transit_receiver.get_relay_hints(), }, }).encode("utf-8") - r = Receiver(APPID, mydata, so.parent["relay-url"]) - code = so["code"] + r = Receiver(APPID, mydata, args.relay_url) + code = args.code if not code: code = r.input_code("Enter receive-file wormhole code: ") r.set_code(code) @@ -44,7 +44,7 @@ def receive_file(so): print("Receiving %d bytes for '%s' (%s).." % (filesize, filename, transit_receiver.describe())) - target = so["output-file"] + target = args.output_file if not target: # allow the sender to specify the filename, but only write to the # current directory, and never overwrite anything diff --git a/src/wormhole/scripts/cmd_receive_text.py b/src/wormhole/scripts/cmd_receive_text.py index a8215d4..c338b64 100644 --- a/src/wormhole/scripts/cmd_receive_text.py +++ b/src/wormhole/scripts/cmd_receive_text.py @@ -4,11 +4,11 @@ from wormhole.blocking.transcribe import Receiver, WrongPasswordError APPID = "lothar.com/wormhole/text-xfer" -def receive_text(so): +def receive_text(args): # we're receiving data = json.dumps({"message": "ok"}).encode("utf-8") - r = Receiver(APPID, data, so.parent["relay-url"]) - code = so["code"] + r = Receiver(APPID, data, args.relay_url) + code = args.code if not code: code = r.input_code("Enter receive-text wormhole code: ") r.set_code(code) diff --git a/src/wormhole/scripts/cmd_send_file.py b/src/wormhole/scripts/cmd_send_file.py index 12d8027..33e714f 100644 --- a/src/wormhole/scripts/cmd_send_file.py +++ b/src/wormhole/scripts/cmd_send_file.py @@ -6,11 +6,11 @@ from .progress import start_progress, update_progress, finish_progress APPID = "lothar.com/wormhole/file-xfer" -def send_file(so): +def send_file(args): # we're sending - filename = so["filename"] + filename = args.filename assert os.path.isfile(filename) - transit_sender = TransitSender(transit_relay=so.parent["transit-helper"]) + transit_sender = TransitSender(transit_relay=args.transit_helper) filesize = os.stat(filename).st_size data = json.dumps({ @@ -24,7 +24,7 @@ def send_file(so): }, }).encode("utf-8") - i = Initiator(APPID, data, so.parent["relay-url"]) + i = Initiator(APPID, data, args.relay_url) code = i.get_code() print("On the other computer, please run: wormhole receive-file") print("Wormhole code is '%s'" % code) diff --git a/src/wormhole/scripts/cmd_send_text.py b/src/wormhole/scripts/cmd_send_text.py index 8a8b45c..2822400 100644 --- a/src/wormhole/scripts/cmd_send_text.py +++ b/src/wormhole/scripts/cmd_send_text.py @@ -4,12 +4,12 @@ from wormhole.blocking.transcribe import Initiator, WrongPasswordError APPID = "lothar.com/wormhole/text-xfer" -def send_text(so): +def send_text(args): # we're sending - message = so["text"] + message = args.text data = json.dumps({"message": message, }).encode("utf-8") - i = Initiator(APPID, data, so.parent["relay-url"]) + i = Initiator(APPID, data, args.relay_url) code = i.get_code() print("On the other computer, please run: wormhole receive-text") print("Wormhole code is: %s" % code) diff --git a/src/wormhole/scripts/runner.py b/src/wormhole/scripts/runner.py index b5132ae..5f10d86 100644 --- a/src/wormhole/scripts/runner.py +++ b/src/wormhole/scripts/runner.py @@ -1,93 +1,69 @@ -import sys -from twisted.python import usage +import sys, argparse +from textwrap import dedent from .. import public_relay +from .. import __version__ +from . import cmd_send_text, cmd_receive_text, cmd_send_file, cmd_receive_file -class SendTextOptions(usage.Options): - def parseArgs(self, text): - self["text"] = text - synopsis = "TEXT" +parser = argparse.ArgumentParser( + usage="wormhole SUBCOMMAND (subcommand-options)", + description=dedent(""" + Create a Magic Wormhole and communicate through it. Wormholes are created + by speaking the same magic CODE in two different places at the same time. + Wormholes are secure against anyone who doesn't use the same code."""), + ) +parser.add_argument("--version", action="version", + version="magic-wormhole "+ __version__) +g = parser.add_argument_group("wormhole configuration options") +g.add_argument("--relay-url", default=public_relay.RENDEZVOUS_RELAY, + metavar="URL", help="rendezvous relay to use") +g.add_argument("--transit-helper", default=public_relay.TRANSIT_RELAY, + metavar="tcp:HOST:PORT", help="transit relay to use") +subparsers = parser.add_subparsers(title="subcommands", + dest="subcommand") -class ReceiveTextOptions(usage.Options): - def parseArgs(self, code=None): - self["code"] = code - synopsis = "[CODE]" -class SendFileOptions(usage.Options): - def parseArgs(self, filename): - self["filename"] = filename - synopsis = "FILENAME" +p = subparsers.add_parser("send-text", description="Send a text mesasge", + usage="wormhole send-text TEXT") +p.add_argument("text", metavar="TEXT", help="the message to send (a string)") +p.set_defaults(func=cmd_send_text.send_text) -class ReceiveFileOptions(usage.Options): - optParameters = [ - ["output-file", "o", None, "File to create"], - ] - def parseArgs(self, code=None): - self["code"] = code - synopsis = "[CODE]" +p = subparsers.add_parser("receive-text", description="Receive a text message", + usage="wormhole receive-text [CODE]") +p.add_argument("code", nargs="?", default=None, metavar="[CODE]", + help=dedent("""\ + The magic-wormhole code, from the sender. If omitted, the + program will ask for it, using tab-completion."""), + ) +p.set_defaults(func=cmd_receive_text.receive_text) -class Options(usage.Options): - synopsis = "\nUsage: wormhole " - optParameters = [ - ["relay-url", None, public_relay.RENDEZVOUS_RELAY, - "rendezvous relay to use (URL)"], - ["transit-helper", None, public_relay.TRANSIT_RELAY, - "transit relay to use (tcp:HOST:PORT)"], - ] - subCommands = [("send-text", None, SendTextOptions, "Send a text message"), - ("send-file", None, SendFileOptions, "Send a file"), - ("receive-text", None, ReceiveTextOptions, "Receive a text message"), - ("receive-file", None, ReceiveFileOptions, "Receive a file"), - ] +p = subparsers.add_parser("send-file", description="Send a file", + usage="wormhole send-file FILENAME") +p.add_argument("filename", metavar="FILENAME", help="The file to be sent") +p.set_defaults(func=cmd_send_file.send_file) - def getUsage(self, **kwargs): - t = usage.Options.getUsage(self, **kwargs) - return t + "\nPlease run 'wormhole --help' for more details on each command.\n" +p = subparsers.add_parser("receive-file", description="Receive a file", + usage="wormhole receive-file [-o FILENAME] [CODE]") +p.add_argument("-o", "--output-file", default=None, metavar="FILENAME", + help=dedent("""\ + The file to create, overriding the filename suggested by the + sender"""), + ) +p.add_argument("code", nargs="?", default=None, metavar="[CODE]", + help=dedent("""\ + The magic-wormhole code, from the sender. If omitted, the + program will ask for it, using tab-completion."""), + ) +p.set_defaults(func=cmd_receive_file.receive_file) - def postOptions(self): - if not hasattr(self, 'subOptions'): - raise usage.UsageError("must specify a command") - -def send_text(*args): - from . import cmd_send_text - return cmd_send_text.send_text(*args) - -def receive_text(*args): - from . import cmd_receive_text - return cmd_receive_text.receive_text(*args) - -def send_file(*args): - from . import cmd_send_file - return cmd_send_file.send_file(*args) - -def receive_file(*args): - from . import cmd_receive_file - return cmd_receive_file.receive_file(*args) - -DISPATCH = {"send-text": send_text, - "receive-text": receive_text, - "send-file": send_file, - "receive-file": receive_file, - } def run(args, stdout, stderr, executable=None): """This is invoked directly by the 'wormhole' entry-point script. It can also invoked by entry() below.""" - config = Options() + + args = parser.parse_args() try: - config.parseOptions(args) - except usage.error, e: - c = config - while hasattr(c, 'subOptions'): - c = c.subOptions - print >>stderr, str(c) - print >>stderr, e.args[0] - return 1 - command = config.subCommand - so = config.subOptions - so["executable"] = executable - try: - #rc = DISPATCH[command](so, stdout, stderr) - rc = DISPATCH[command](so) + #rc = command.func(args, stdout, stderr) + rc = args.func(args) return rc except ImportError, e: print >>stderr, "--- ImportError ---" @@ -100,3 +76,7 @@ def entry(): """This is used by a setuptools entry_point. When invoked this way, setuptools has already put the installed package on sys.path .""" return run(sys.argv[1:], sys.stdout, sys.stderr, executable=sys.argv[0]) + +if __name__ == "__main__": + args = parser.parse_args() + print args