Last active
December 15, 2015 23:49
-
-
Save shabbyrobe/5342871 to your computer and use it in GitHub Desktop.
Mostly converts the existing testcases.docopt file into format proposed by RFC.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import json | |
import sys | |
def read_doc(h, line): | |
doc = line | |
if re.search(r"\"\"\"\s*$", doc): | |
return doc | |
for line in h: | |
doc += line | |
if re.search(r"(?<!r)\"\"\"\s*$", line): | |
break | |
doc = re.sub(r"\s*\"\"\"\s*$", "\1\n\"\"\"", doc) | |
return doc | |
def read_case(h, line): | |
test = line.strip() | |
if test.startswith('$ prog '): | |
test = test[7:] | |
return (test, read_expect(h)) | |
def read_expect(h): | |
expect = "" | |
for line in h: | |
if line.rstrip() == '': | |
break | |
else: | |
expect += line | |
return expect.rstrip() | |
if __name__ == "__main__": | |
tokens = [] | |
with open(sys.argv[1]) as h: | |
for line in h: | |
if re.match(r"^\s*#", line): | |
tokens.append(('comment', line)) | |
elif re.match(r'^r"""', line): | |
tokens.append(('doc', read_doc(h, line))) | |
elif re.match(r'^\$', line): | |
tokens.append(('case', read_case(h, line))) | |
else: | |
if line.rstrip() != '': | |
raise RuntimeError("Line was not empty: %s" % line.rstrip()) | |
for name, value in tokens: | |
if name == 'comment': | |
print value.strip() | |
if name == 'doc': | |
print value.strip() | |
elif name == 'case': | |
test, expect = value | |
print "docopt %s" % json.dumps({'argv': test}) | |
print "expect %s" % (json.dumps({'DocoptExit': True}) if expect == '"user-error"' else expect) | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
r"""Usage: prog | |
""" | |
docopt {"argv": "$ prog"} | |
expect {} | |
docopt {"argv": "--xxx"} | |
expect {"DocoptExit": true} | |
r"""Usage: prog [options] | |
Options: -a All. | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"-a": false} | |
docopt {"argv": "-a"} | |
expect {"-a": true} | |
docopt {"argv": "-x"} | |
expect {"DocoptExit": true} | |
r"""Usage: prog [options] | |
Options: --all All. | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"--all": false} | |
docopt {"argv": "--all"} | |
expect {"--all": true} | |
docopt {"argv": "--xxx"} | |
expect {"DocoptExit": true} | |
r"""Usage: prog [options] | |
Options: -v, --verbose Verbose. | |
""" | |
docopt {"argv": "--verbose"} | |
expect {"--verbose": true} | |
docopt {"argv": "--ver"} | |
expect {"--verbose": true} | |
docopt {"argv": "-v"} | |
expect {"--verbose": true} | |
r"""Usage: prog [options] | |
Options: -p PATH | |
""" | |
docopt {"argv": "-p home/"} | |
expect {"-p": "home/"} | |
docopt {"argv": "-phome/"} | |
expect {"-p": "home/"} | |
docopt {"argv": "-p"} | |
expect {"DocoptExit": true} | |
r"""Usage: prog [options] | |
Options: --path <path> | |
""" | |
docopt {"argv": "--path home/"} | |
expect {"--path": "home/"} | |
docopt {"argv": "--path=home/"} | |
expect {"--path": "home/"} | |
docopt {"argv": "--pa home/"} | |
expect {"--path": "home/"} | |
docopt {"argv": "--pa=home/"} | |
expect {"--path": "home/"} | |
docopt {"argv": "--path"} | |
expect {"DocoptExit": true} | |
r"""Usage: prog [options] | |
Options: -p PATH, --path=<path> Path to files. | |
""" | |
docopt {"argv": "-proot"} | |
expect {"--path": "root"} | |
r"""Usage: prog [options] | |
Options: -p --path PATH Path to files. | |
""" | |
docopt {"argv": "-p root"} | |
expect {"--path": "root"} | |
docopt {"argv": "--path root"} | |
expect {"--path": "root"} | |
r"""Usage: prog [options] | |
Options: | |
-p PATH Path to files [default: ./] | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"-p": "./"} | |
docopt {"argv": "-phome"} | |
expect {"-p": "home"} | |
r"""UsAgE: prog [options] | |
OpTiOnS: --path=<files> Path to files | |
[dEfAuLt: /root] | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"--path": "/root"} | |
docopt {"argv": "--path=home"} | |
expect {"--path": "home"} | |
r"""usage: prog [options] | |
options: | |
-a Add | |
-r Remote | |
-m <msg> Message | |
""" | |
docopt {"argv": "-a -r -m Hello"} | |
expect {"-a": true, | |
"-r": true, | |
"-m": "Hello"} | |
docopt {"argv": "-armyourass"} | |
expect {"-a": true, | |
"-r": true, | |
"-m": "yourass"} | |
docopt {"argv": "-a -r"} | |
expect {"-a": true, | |
"-r": true, | |
"-m": null} | |
r"""Usage: prog [options] | |
Options: --version | |
--verbose | |
""" | |
docopt {"argv": "--version"} | |
expect {"--version": true, | |
"--verbose": false} | |
docopt {"argv": "--verbose"} | |
expect {"--version": false, | |
"--verbose": true} | |
docopt {"argv": "--ver"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "--verb"} | |
expect {"--version": false, | |
"--verbose": true} | |
r"""usage: prog [-a -r -m <msg>] | |
options: | |
-a Add | |
-r Remote | |
-m <msg> Message | |
""" | |
docopt {"argv": "-armyourass"} | |
expect {"-a": true, | |
"-r": true, | |
"-m": "yourass"} | |
r"""usage: prog [-armmsg] | |
options: -a Add | |
-r Remote | |
-m <msg> Message | |
""" | |
docopt {"argv": "-a -r -m Hello"} | |
expect {"-a": true, | |
"-r": true, | |
"-m": "Hello"} | |
r"""usage: prog -a -b | |
options: | |
-a | |
-b | |
""" | |
docopt {"argv": "-a -b"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-b -a"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-a"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog (-a -b) | |
options: -a | |
-b | |
""" | |
docopt {"argv": "-a -b"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-b -a"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-a"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [-a] -b | |
options: -a | |
-b | |
""" | |
docopt {"argv": "-a -b"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-b -a"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-a"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "-b"} | |
expect {"-a": false, "-b": true} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [(-a -b)] | |
options: -a | |
-b | |
""" | |
docopt {"argv": "-a -b"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-b -a"} | |
expect {"-a": true, "-b": true} | |
docopt {"argv": "-a"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "-b"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"-a": false, "-b": false} | |
r"""usage: prog (-a|-b) | |
options: -a | |
-b | |
""" | |
docopt {"argv": "-a -b"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "-a"} | |
expect {"-a": true, "-b": false} | |
docopt {"argv": "-b"} | |
expect {"-a": false, "-b": true} | |
r"""usage: prog [ -a | -b ] | |
options: -a | |
-b | |
""" | |
docopt {"argv": "-a -b"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"-a": false, "-b": false} | |
docopt {"argv": "-a"} | |
expect {"-a": true, "-b": false} | |
docopt {"argv": "-b"} | |
expect {"-a": false, "-b": true} | |
r"""usage: prog <arg>""" | |
docopt {"argv": "10"} | |
expect {"<arg>": "10"} | |
docopt {"argv": "10 20"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [<arg>]""" | |
docopt {"argv": "10"} | |
expect {"<arg>": "10"} | |
docopt {"argv": "10 20"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"<arg>": null} | |
r"""usage: prog <kind> <name> <type>""" | |
docopt {"argv": "10 20 40"} | |
expect {"<kind>": "10", "<name>": "20", "<type>": "40"} | |
docopt {"argv": "10 20"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog <kind> [<name> <type>]""" | |
docopt {"argv": "10 20 40"} | |
expect {"<kind>": "10", "<name>": "20", "<type>": "40"} | |
docopt {"argv": "10 20"} | |
expect {"<kind>": "10", "<name>": "20", "<type>": null} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [<kind> | <name> <type>]""" | |
docopt {"argv": "10 20 40"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "20 40"} | |
expect {"<kind>": null, "<name>": "20", "<type>": "40"} | |
docopt {"argv": "$ prog"} | |
expect {"<kind>": null, "<name>": null, "<type>": null} | |
r"""usage: prog (<kind> --all | <name>) | |
options: | |
--all | |
""" | |
docopt {"argv": "10 --all"} | |
expect {"<kind>": "10", "--all": true, "<name>": null} | |
docopt {"argv": "10"} | |
expect {"<kind>": null, "--all": false, "<name>": "10"} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [<name> <name>]""" | |
docopt {"argv": "10 20"} | |
expect {"<name>": ["10", "20"]} | |
docopt {"argv": "10"} | |
expect {"<name>": ["10"]} | |
docopt {"argv": "$ prog"} | |
expect {"<name>": []} | |
r"""usage: prog [(<name> <name>)]""" | |
docopt {"argv": "10 20"} | |
expect {"<name>": ["10", "20"]} | |
docopt {"argv": "10"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "$ prog"} | |
expect {"<name>": []} | |
r"""usage: prog NAME...""" | |
docopt {"argv": "10 20"} | |
expect {"NAME": ["10", "20"]} | |
docopt {"argv": "10"} | |
expect {"NAME": ["10"]} | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [NAME]...""" | |
docopt {"argv": "10 20"} | |
expect {"NAME": ["10", "20"]} | |
docopt {"argv": "10"} | |
expect {"NAME": ["10"]} | |
docopt {"argv": "$ prog"} | |
expect {"NAME": []} | |
r"""usage: prog [NAME...]""" | |
docopt {"argv": "10 20"} | |
expect {"NAME": ["10", "20"]} | |
docopt {"argv": "10"} | |
expect {"NAME": ["10"]} | |
docopt {"argv": "$ prog"} | |
expect {"NAME": []} | |
r"""usage: prog [NAME [NAME ...]]""" | |
docopt {"argv": "10 20"} | |
expect {"NAME": ["10", "20"]} | |
docopt {"argv": "10"} | |
expect {"NAME": ["10"]} | |
docopt {"argv": "$ prog"} | |
expect {"NAME": []} | |
r"""usage: prog (NAME | --foo NAME) | |
options: --foo | |
""" | |
docopt {"argv": "10"} | |
expect {"NAME": "10", "--foo": false} | |
docopt {"argv": "--foo 10"} | |
expect {"NAME": "10", "--foo": true} | |
docopt {"argv": "--foo=10"} | |
expect {"DocoptExit": true} | |
r"""usage: prog (NAME | --foo) [--bar | NAME] | |
options: --foo | |
options: --bar | |
""" | |
docopt {"argv": "10"} | |
expect {"NAME": ["10"], "--foo": false, "--bar": false} | |
docopt {"argv": "10 20"} | |
expect {"NAME": ["10", "20"], "--foo": false, "--bar": false} | |
docopt {"argv": "--foo --bar"} | |
expect {"NAME": [], "--foo": true, "--bar": true} | |
r"""Naval Fate. | |
Usage: | |
prog ship new <name>... | |
prog ship [<name>] move <x> <y> [--speed=<kn>] | |
prog ship shoot <x> <y> | |
prog mine (set|remove) <x> <y> [--moored|--drifting] | |
prog -h | --help | |
prog --version | |
Options: | |
-h --help Show this screen. | |
--version Show version. | |
--speed=<kn> Speed in knots [default: 10]. | |
--moored Mored (anchored) mine. | |
--drifting Drifting mine. | |
""" | |
docopt {"argv": "ship Guardian move 150 300 --speed=20"} | |
expect {"--drifting": false, | |
"--help": false, | |
"--moored": false, | |
"--speed": "20", | |
"--version": false, | |
"<name>": ["Guardian"], | |
"<x>": "150", | |
"<y>": "300", | |
"mine": false, | |
"move": true, | |
"new": false, | |
"remove": false, | |
"set": false, | |
"ship": true, | |
"shoot": false} | |
r"""usage: prog --hello""" | |
docopt {"argv": "--hello"} | |
expect {"--hello": true} | |
r"""usage: prog [--hello=<world>]""" | |
docopt {"argv": "$ prog"} | |
expect {"--hello": null} | |
docopt {"argv": "--hello wrld"} | |
expect {"--hello": "wrld"} | |
r"""usage: prog [-o]""" | |
docopt {"argv": "$ prog"} | |
expect {"-o": false} | |
docopt {"argv": "-o"} | |
expect {"-o": true} | |
r"""usage: prog [-opr]""" | |
docopt {"argv": "-op"} | |
expect {"-o": true, "-p": true, "-r": false} | |
r"""usage: prog --aabb | --aa""" | |
docopt {"argv": "--aa"} | |
expect {"--aabb": false, "--aa": true} | |
docopt {"argv": "--a"} | |
expect "user-error" # not a unique prefix | |
# | |
# Counting number of flags | |
# | |
r"""Usage: prog -v""" | |
docopt {"argv": "-v"} | |
expect {"-v": true} | |
r"""Usage: prog [-v -v]""" | |
docopt {"argv": "$ prog"} | |
expect {"-v": 0} | |
docopt {"argv": "-v"} | |
expect {"-v": 1} | |
docopt {"argv": "-vv"} | |
expect {"-v": 2} | |
r"""Usage: prog -v ...""" | |
docopt {"argv": "$ prog"} | |
expect {"DocoptExit": true} | |
docopt {"argv": "-v"} | |
expect {"-v": 1} | |
docopt {"argv": "-vv"} | |
expect {"-v": 2} | |
docopt {"argv": "-vvvvvv"} | |
expect {"-v": 6} | |
r"""Usage: prog [-v | -vv | -vvv] | |
This one is probably most readable user-friednly variant. | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"-v": 0} | |
docopt {"argv": "-v"} | |
expect {"-v": 1} | |
docopt {"argv": "-vv"} | |
expect {"-v": 2} | |
docopt {"argv": "-vvvv"} | |
expect {"DocoptExit": true} | |
r"""usage: prog [--ver --ver]""" | |
docopt {"argv": "--ver --ver"} | |
expect {"--ver": 2} | |
# | |
# Counting commands | |
# | |
r"""usage: prog [go]""" | |
docopt {"argv": "go"} | |
expect {"go": true} | |
r"""usage: prog [go go]""" | |
docopt {"argv": "$ prog"} | |
expect {"go": 0} | |
docopt {"argv": "go"} | |
expect {"go": 1} | |
docopt {"argv": "go go"} | |
expect {"go": 2} | |
docopt {"argv": "go go go"} | |
expect {"DocoptExit": true} | |
r"""usage: prog go...""" | |
docopt {"argv": "go go go go go"} | |
expect {"go": 5} | |
# | |
# Test [options] shourtcut | |
# | |
r"""Usage: prog [options] A | |
Options: | |
-q Be quiet | |
-v Be verbose. | |
""" | |
docopt {"argv": "arg"} | |
expect {"A": "arg", "-v": false, "-q": false} | |
docopt {"argv": "-v arg"} | |
expect {"A": "arg", "-v": true, "-q": false} | |
docopt {"argv": "-q arg"} | |
expect {"A": "arg", "-v": false, "-q": true} | |
# | |
# Test single dash | |
# | |
r"""usage: prog [-]""" | |
docopt {"argv": "-"} | |
expect {"-": true} | |
docopt {"argv": "$ prog"} | |
expect {"-": false} | |
# | |
# If argument is repeated, its value should always be a list | |
# | |
r"""usage: prog [NAME [NAME ...]]""" | |
docopt {"argv": "a b"} | |
expect {"NAME": ["a", "b"]} | |
docopt {"argv": "$ prog"} | |
expect {"NAME": []} | |
# | |
# Option's argument defaults to null/None | |
# | |
r"""usage: prog [options] | |
options: | |
-a Add | |
-m <msg> Message | |
""" | |
docopt {"argv": "-a"} | |
expect {"-m": null, "-a": true} | |
# | |
# Test options without description | |
# | |
r"""usage: prog --hello""" | |
docopt {"argv": "--hello"} | |
expect {"--hello": true} | |
r"""usage: prog [--hello=<world>]""" | |
docopt {"argv": "$ prog"} | |
expect {"--hello": null} | |
docopt {"argv": "--hello wrld"} | |
expect {"--hello": "wrld"} | |
r"""usage: prog [-o]""" | |
docopt {"argv": "$ prog"} | |
expect {"-o": false} | |
docopt {"argv": "-o"} | |
expect {"-o": true} | |
r"""usage: prog [-opr]""" | |
docopt {"argv": "-op"} | |
expect {"-o": true, "-p": true, "-r": false} | |
r"""usage: git [-v | --verbose]""" | |
docopt {"argv": "-v"} | |
expect {"-v": true, "--verbose": false} | |
r"""usage: git remote [-v | --verbose]""" | |
docopt {"argv": "remote -v"} | |
expect {"remote": true, "-v": true, "--verbose": false} | |
# | |
# Test empty usage pattern | |
# | |
r"""usage: prog""" | |
docopt {"argv": "$ prog"} | |
expect {} | |
r"""usage: prog | |
prog <a> <b> | |
""" | |
docopt {"argv": "1 2"} | |
expect {"<a>": "1", "<b>": "2"} | |
docopt {"argv": "$ prog"} | |
expect {"<a>": null, "<b>": null} | |
r"""usage: prog <a> <b> | |
prog | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"<a>": null, "<b>": null} | |
# | |
# Option's argument should not capture default value from usage pattern | |
# | |
r"""usage: prog [--file=<f>]""" | |
docopt {"argv": "$ prog"} | |
expect {"--file": null} | |
r"""usage: prog [--file=<f>] | |
options: --file <a> | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"--file": null} | |
r"""Usage: prog [-a <host:port>] | |
Options: -a, --address <host:port> TCP address [default: localhost:6283]. | |
""" | |
docopt {"argv": "$ prog"} | |
expect {"--address": "localhost:6283"} | |
# | |
# If option with argument could be repeated, | |
# its arguments should be accumulated into a list | |
# | |
r"""usage: prog --long=<arg> ...""" | |
docopt {"argv": "--long one"} | |
expect {"--long": ["one"]} | |
docopt {"argv": "--long one --long two"} | |
expect {"--long": ["one", "two"]} | |
# | |
# Test multiple elements repeated at once | |
# | |
r"""usage: prog (go <direction> --speed=<km/h>)...""" | |
docopt {"argv": " go left --speed=5 go right --speed=9"} | |
expect {"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]} | |
# | |
# Required options should work with option shortcut | |
# | |
r"""usage: prog [options] -a | |
options: -a | |
""" | |
docopt {"argv": "-a"} | |
expect {"-a": true} | |
# | |
# If option could be repeated its defaults should be split into a list | |
# | |
r"""usage: prog [-o <o>]... | |
options: -o <o> [default: x] | |
""" | |
docopt {"argv": "-o this -o that"} | |
expect {"-o": ["this", "that"]} | |
docopt {"argv": "$ prog"} | |
expect {"-o": ["x"]} | |
r"""usage: prog [-o <o>]... | |
options: -o <o> [default: x y] | |
""" | |
docopt {"argv": "-o this"} | |
expect {"-o": ["this"]} | |
docopt {"argv": "$ prog"} | |
expect {"-o": ["x", "y"]} | |
# | |
# Test stacked option's argument | |
# | |
r"""usage: prog -pPATH | |
options: -p PATH | |
""" | |
docopt {"argv": "-pHOME"} | |
expect {"-p": "HOME"} | |
# | |
# Issue 56: Repeated mutually exclusive args give nested lists sometimes | |
# | |
r"""Usage: foo (--xx=x|--yy=y)...""" | |
docopt {"argv": "--xx=1 --yy=2"} | |
expect {"--xx": ["1"], "--yy": ["2"]} | |
# | |
# POSIXly correct tokenization | |
# | |
r"""usage: prog [<input file>]""" | |
docopt {"argv": "f.txt"} | |
expect {"<input file>": "f.txt"} | |
r"""usage: prog [--input=<file name>]...""" | |
docopt {"argv": "--input a.txt --input=b.txt"} | |
expect {"--input": ["a.txt", "b.txt"]} | |
# | |
# Issue 85: `[options]` shourtcut with multiple subcommands | |
# | |
r"""usage: prog good [options] | |
prog fail [options] | |
options: --loglevel=N | |
""" | |
docopt {"argv": "fail --loglevel 5"} | |
expect {"--loglevel": "5", "fail": true, "good": false} | |
# | |
# Usage-section syntax | |
# | |
r"""usage:prog --foo""" | |
docopt {"argv": "--foo"} | |
expect {"--foo": true} | |
r"""PROGRAM USAGE: prog --foo""" | |
docopt {"argv": "--foo"} | |
expect {"--foo": true} | |
r"""Usage: prog --foo | |
prog --bar | |
NOT PART OF SECTION | |
""" | |
docopt {"argv": "--foo"} | |
expect {"--foo": true, "--bar": false} | |
r"""Usage: | |
prog --foo | |
prog --bar | |
NOT PART OF SECTION | |
""" | |
docopt {"argv": "--foo"} | |
expect {"--foo": true, "--bar": false} | |
r"""Usage: | |
prog --foo | |
prog --bar | |
NOT PART OF SECTION | |
""" | |
docopt {"argv": "--foo"} | |
expect {"--foo": true, "--bar": false} | |
# | |
# Options-section syntax | |
# | |
r"""Usage: prog [options] | |
global options: --foo | |
local options: --baz | |
--bar | |
other options: | |
--egg | |
--spam | |
-not-an-option- | |
""" | |
docopt {"argv": "--baz --egg"} | |
expect {"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment