rewrite CLI tools to use argparse, remove Twisted dependency

We used to use twisted.python.usage.Options, hence we depended upon
Twisted. Now we depend upon "argparse" instead, which is in the py2.7
stdlib (and on pypi for 2.6). This package will still (eventually)
provide Twisted support, but applications which need it will already
express a dependency on twisted themselves, so by removing the
dependency here, we make life easier for applications that don't use it.
This commit is contained in:
Brian Warner 2015-03-22 16:52:35 -07:00
parent 9e7d807171
commit 8e456dea5e
6 changed files with 73 additions and 93 deletions

View File

@ -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,
)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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 <command>"
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 <command> --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