comparison shelltools/query-pr/query.py @ 52:c0be30249ffe

Fix the argument handling.
author David A. Holland
date Sat, 02 Apr 2022 21:13:01 -0400
parents ef6d572c4e1e
children 40f64a96481f
comparison
equal deleted inserted replaced
51:ef6d572c4e1e 52:c0be30249ffe
2 2
3 import sys 3 import sys
4 import argparse 4 import argparse
5 import psycopg2 5 import psycopg2
6 6
7 program_description = """
8 Search for and retrieve problem reports.
9 """
10 program_version = "@VERSION@" 7 program_version = "@VERSION@"
11 8
12 ############################################################ 9 ############################################################
13 # settings 10 # settings
14 11
15 outfile = sys.stdout 12 outfile = sys.stdout
16 outmaterial = "headers" 13 outmaterial = "headers"
17 outformat = "text" 14 outformat = "text"
15
16 ############################################################
17 # simple dump widget
18
19 class Dumper:
20 def __init__(self, f):
21 self.f = f
22 self.indentation = 0
23 def indent(self):
24 self.indentation += 3
25 def unindent(self):
26 self.indentation -= 3
27 def write(self, msg):
28 self.f.write(" " * self.indentation + msg + "\n")
18 29
19 ############################################################ 30 ############################################################
20 # database field access 31 # database field access
21 32
22 # 33 #
665 # XXX need an http output mode so we can send the mimetype! 676 # XXX need an http output mode so we can send the mimetype!
666 sys.stdout.write(body) 677 sys.stdout.write(body)
667 # end print_attachment 678 # end print_attachment
668 679
669 ############################################################ 680 ############################################################
670 # AST for query 681 # AST for input query
671 682
672 class Invocation: 683 class Invocation:
684 Q_TERM = 1
685 Q_SQL = 2
673 class Query: 686 class Query:
674 Q_TERM = 1 # XXX unused so far
675 Q_SQL = 2
676 Q_AND = 3
677 Q_OR = 4
678 Q_TSTRING = 5
679 Q_QSTRING = 6
680
681 def __init__(self, type): 687 def __init__(self, type):
682 self.type = type 688 self.type = type
683 def doterm(term): 689 def dump(self, d):
684 self = Query(Q_TERM) 690 if self.type == Invocation.Q_TERM:
685 self.term = term 691 d.write("query.term({})".format(self.term))
686 return self 692 else:
687 def dosql(s): 693 d.write("query.sql({})".format(self.sql))
688 self = Query(Q_SQL) 694 def mkterm(term):
689 self.sql = s 695 self = Invocation.Query(Invocation.Q_TERM)
690 return self 696 self.term = term
691 def doand(qs): 697 return self
692 self = Query(Q_AND) 698 def mksql(s):
693 self.args = qs 699 self = Invocation.Query(Invocation.Q_SQL)
694 return self 700 self.sql = s
695 def door(qs): 701 return self
696 self = Query(Q_OR)
697 self.args = qs
698 return self
699 # query term string
700 def dotstring(q):
701 self = Query(Q_TSTRING)
702 self.string = q
703 return self
704 # whole query string
705 def doqstring(q):
706 self = Query(Q_QSTRING)
707 self.string = q
708 return self
709 # end class Query
710 702
711 class Order: 703 class Order:
712 def __init__(self, field, rev = False): 704 def __init__(self, field, rev = False):
713 self.field = field 705 self.field = field
714 self.rev = rev 706 self.rev = rev
715 def dooldest(ign): 707 def dump(self, d):
716 return Order("number") 708 d.write("order({}, {})".format(self.field, self.rev))
717 def donewest(ign): 709 def mkoldest():
718 return Order("number", True) 710 return Invocation.Order("number")
719 def dostaleness(ign): 711 def mknewest():
720 return Order("modified_date", True) 712 return Invocation.Order("number", True)
721 def dofield(field): 713 def mkstaleness():
722 return Order(field) 714 return Invocation.Order("modified_date", True)
723 def dorevfield(field): 715 def mkfield(field):
724 return Order(field, True) 716 return Invocation.Order(field)
725 # end class Order 717 def mkrevfield(field):
718 return Invocation.Order(field, True)
726 719
727 class Search: 720 class Search:
728 def __init__(self, qs, openonly, publiconly, os): 721 def __init__(self, qs, openonly, publiconly, os):
729 self.queries = qs 722 self.queries = qs
730 self.openonly = openonly 723 self.openonly = openonly
731 self.publiconly = publiconly 724 self.publiconly = publiconly
732 self.orders = os 725 self.orders = os
733 # end class Search 726 def dump(self, d):
734 727 d.write("search({}, {})".format(
728 "open" if self.openonly else "closed",
729 "public" if self.publiconly else "privileged"))
730 d.indent()
731 d.write("queries")
732 d.indent()
733 for query in self.queries:
734 query.dump(d)
735 d.unindent()
736 d.write("orders")
737 d.indent()
738 for order in self.orders:
739 order.dump(d)
740 d.unindent()
741 d.unindent()
742
743 S_PR = 1
744 S_MESSAGE = 2
745 S_ATTACHMENT = 3
735 class Selection: 746 class Selection:
736 S_PR = 1
737 S_MESSAGE = 2
738 S_ATTACHMENT = 3
739
740 def __init__(self, type): 747 def __init__(self, type):
741 self.type = type 748 self.type = type
742 def dopr(output, outformat): 749 def dump(self, d):
743 self = Selection(S_PR) 750 if self.type == Invocation.S_PR:
744 self.output = output 751 d.write("selection.pr({}, {})".format(
745 self.outformat = outformat 752 self.output, self.outformat))
746 return self 753 elif self.type == Invocation.S_MESSAGE:
747 def domessage(arg): 754 d.write("selection.message({})".format(
748 self = Selection(S_MESSAGE) 755 self.message))
749 self.message = arg 756 else:
750 return self 757 d.write("selection.attachment({})".format(
751 def doattachment(arg): 758 self.attachment))
752 self = Selection(S_ATTACHMENT) 759 def mkpr(output, outformat):
753 self.attachment = arg 760 self = Invocation.Selection(Invocation.S_PR)
754 return self 761 self.output = output
755 # end class Selection 762 self.outformat = outformat
756 763 return self
764 def mkmessage(arg):
765 self = Invocation.Selection(Invocation.S_MESSAGE)
766 self.message = arg
767 return self
768 def mkattachment(arg):
769 self = Invocation.Selection(Invocation.S_ATTACHMENT)
770 self.attachment = arg
771 return self
772
773 OP_FIELDS = 1
774 OP_SHOW = 2
775 OP_RANGE = 3
776 OP_SEARCH = 4
757 class Op: 777 class Op:
758 # operation codes
759 OP_FIELDS = 1
760 OP_SHOW = 2
761 OP_RANGE = 3
762 OP_SEARCH = 4
763
764 def __init__(self, type): 778 def __init__(self, type):
765 self.type = type 779 self.type = type
766 780 def dump(self, d):
767 def dofields(): 781 if self.type == Invocation.OP_FIELDS:
768 return Op(OP_FIELDS) 782 d.write("op.fields")
769 def doshow(field): 783 elif self.type == Invocation.OP_SHOW:
770 self = Op(OP_SHOW) 784 d.write("op.show({})".format(self.field))
771 self.field = field 785 elif self.type == Invocation.OP_RANGE:
772 return self 786 d.write("op.range({})".format(self.field))
773 def dorange(field): 787 else:
774 self = Op(OP_RANGE) 788 d.write("op.search:")
775 self.field = field 789 d.indent()
776 return self 790 self.search.dump(d)
777 def dosearch(s, sels): 791 for sel in self.sels:
778 self = Op(OP_SEARCH) 792 sel.dump(d)
779 self.search = s 793 d.unindent()
780 self.sels = sels 794 def mkfields():
781 return self 795 return Invocation.Op(Invocation.OP_FIELDS)
782 # end class Op 796 def mkshow(field):
797 self = Invocation.Op(Invocation.OP_SHOW)
798 self.field = field
799 return self
800 def mkrange(field):
801 self = Invocation.Op(Invocation.OP_RANGE)
802 self.field = field
803 return self
804 def mksearch(s, sels):
805 self = Invocation.Op(Invocation.OP_SEARCH)
806 self.search = s
807 self.sels = sels
808 return self
783 809
784 def __init__(self, ops): 810 def __init__(self, ops):
785 self.ops = ops 811 self.ops = ops
812 def dump(self, d):
813 d.write("invocation: {} ops".format(len(self.ops)))
814 d.indent()
815 for op in self.ops:
816 op.dump(d)
817 d.unindent()
786 # end class Invocation 818 # end class Invocation
787 819
788 ############################################################ 820 ############################################################
789 # run (eval the SQL and print the results) 821 # run (eval the SQL and print the results)
790 822
967 999
968 ############################################################ 1000 ############################################################
969 # arg handling 1001 # arg handling
970 1002
971 # 1003 #
972 # I swear, all getopt interfaces suck. 1004 # I swear, all getopt interfaces suck. You have to write your own to
1005 # not go mad.
973 # 1006 #
974 # Provide an argparse action for constructing something out of the 1007
975 # argument value and appending that somewhere, since it can't do this 1008 # Usage.
976 # on its own. 1009 def usage():
977 # 1010 sys.stderr.write("""
978 # The way you use this: 1011 query-pr: search for and retrieve problem reports
979 # p.add_argument("--foo", action = CtorAppend, dest = 'mylist', 1012 usage: query-pr [options] [searchterms] Query the database.
980 # const = ctor) 1013 query-pr [options] --sql QUERY Execute QUERY as the search.
981 # where ctor is a function taking the option arg(s) and producing 1014 query-pr [options] -s QUERY Same as --sql.
982 # a value to append to mylist. 1015 query-pr --fields List database fields.
983 # 1016 query-pr --show FIELD Print info about database field.
984 # This itself is mangy even for what it is -- it seems like we should 1017 query-pr --range FIELD Print extant range for database field.
985 # be able to pass action=CtorAppend(ctor), but since it has to be a 1018 query-pr --help / -h Print this message.
986 # class that doesn't work... unless you make a new class for every 1019 query-pr --version / -v Print version and exit.
987 # ctor you want to use, which seems completely insane. 1020 options:
988 # 1021 --search-string STRING Forcibly treat STRING as a search term.
989 class CtorAppend(argparse.Action): 1022 --message NUM / -m NUM Print a message by its ID number.
990 def __call__(self, parser, namespace, values, option_string=None): 1023 --attachment NUM / -a NUM Print an attachment by its ID number.
991 items = getattr(namespace, self.dest) 1024 --paranoid Deny unsafe settings.
992 item = self.const(values) 1025 filter options:
993 items.append(item) 1026 --open Exclude closed PRs. (default)
994 setattr(namespace, self.dest, items) 1027 --closed Include closed PRs.
995 1028 --public Exclude confidential PRs.
996 def getargs(): 1029 --privileged Include confidential PRs. (default)
997 p = argparse.ArgumentParser(program_description) 1030 sort options:
998 1031 --oldest Sort with oldest PRs first. (default)
999 # note: -h/--help is built in by default 1032 --newest Sort with newest PRs first.
1000 p.add_argument("-v", "--version", 1033 --staleness Sort by last modification time.
1001 action='version', version=program_version, 1034 --orderby FIELD Sort by specific field.
1002 help="Print program version and exit") 1035 output options:
1003 1036 --raw / -r Print raw SQL output.
1004 p.add_argument("--show", nargs=1, 1037 --list / -l Print in list form.
1005 action=CtorAppend, dest='ops', 1038 --headers Print headers only.
1006 const=Invocation.Op.doshow, 1039 --meta / --metadata Print all metadata.
1007 help="Show description of field") 1040 --full / -f Print entire PR.
1008 p.add_argument("--range", nargs=1, 1041 --text Print text. (default)
1009 action=CtorAppend, dest='ops', 1042 --csv Print CSV.
1010 const=Invocation.Op.dorange, 1043 --xml Print XML.
1011 help="Show range of extant values for field") 1044 --json Print JSON.
1012 1045 --rdf Print RDF.
1013 p.add_argument("--search", nargs=1, 1046 --rdflike Print RDF-like text.
1014 action=CtorAppend, dest='queries', 1047 search terms:
1015 const=Invocation.Query.doqstring, 1048 NUM Single PR by number.
1016 help="Force string to be read as a search string") 1049 NUM-[NUM] Range of PRs by number.
1017 p.add_argument("-s", "--sql", nargs=1, 1050 TEXT Search string
1018 action=CtorAppend, dest='queries', 1051 FIELD:TEXT Search string for a particular field.
1019 const=Invocation.Query.dosql, 1052 FIELD: Use the field's default search string.
1020 help="Supply explicit sql query as search") 1053 derived fields:
1021 1054 arrived-before:DATE Arrival date before DATE.
1022 p.add_argument("--open", 1055 arrived-after:DATE Arrival date after DATE.
1023 action='store_const', dest='openonly', const="True", 1056 closed-before:DATE Close date before DATE.
1024 help="Exclude closed PRs (default)") 1057 closed-after:DATE Close date after DATE, or none.
1025 p.add_argument("--closed", 1058 last-modified-before:DATE Last modified before DATE.
1026 action='store_const', dest='openonly', const="False", 1059 last-modified-after:DATE Last modified after DATE.
1027 help="Include closed PRs in search") 1060 stale:TIME Last modified at least TIME ago.
1028 p.add_argument("--public", 1061 Explicit SQL queries should return lists of PR numbers (only).
1029 action='store_const', dest='publiconly', const="True", 1062 """)
1030 help="Exclude confidential PRs") 1063
1031 p.add_argument("--privileged", 1064 # Widget to hold argv and allow peeling args off one at a time.
1032 action='store_const', dest='publiconly', const="False", 1065 class ArgHolder:
1033 help="Allow confidential PRs (default)") 1066 def __init__(self, argv):
1034 1067 self.argc = len(argv)
1035 p.add_argument("--oldest", 1068 self.argv = argv
1036 action=CtorAppend, dest='orders', 1069 self.pos = 1
1037 const=Invocation.Order.dooldest, 1070 def next(self):
1038 help="Sort output with oldest PRs first") 1071 if self.pos >= self.argc:
1039 p.add_argument("--newest", 1072 return None
1040 action=CtorAppend, dest='orders', 1073 ret = self.argv[self.pos]
1041 const=Invocation.Order.donewest, 1074 self.pos += 1
1042 help="Sort output with newest PRs first") 1075 return ret
1043 p.add_argument("--staleness", 1076 def getarg(self, opt):
1044 action=CtorAppend, dest='orders', 1077 ret = self.next()
1045 const=Invocation.Order.dostaleness, 1078 if ret is None:
1046 help="Sort output by time since last modification") 1079 msg = "Option {} requires an argument\n".format(opt)
1047 p.add_argument("--orderby", nargs=1, 1080 sys.stderr.write(msg)
1048 action=CtorAppend, dest='orders', 1081 exit(1)
1049 const=Invocation.Order.dofield, 1082 return ret
1050 help="Sort output by specific field") 1083
1051 p.add_argument("--revorderby", nargs=1, 1084 # Read the argument list and convert it into an Invocation.
1052 action=CtorAppend, dest='orders', 1085 def getargs(argv):
1053 const=Invocation.Order.dorevfield, 1086 # Results
1054 help="Sort output by specific field, reversed") 1087 ops = []
1055 1088 orders = []
1056 p.add_argument("-m", "--message", nargs=1, 1089 queries = []
1057 action=CtorAppend, dest='selections', 1090 selections = []
1058 const=Invocation.Selection.domessage, 1091 output = "LIST"
1059 help="Print selected message (single PR only)") 1092 outformat = "TEXT"
1060 p.add_argument("-a", "--attachment", nargs=1, 1093 openonly = True
1061 action=CtorAppend, dest='selections', 1094 publiconly = False
1062 const=Invocation.Selection.doattachment, 1095 paranoid = False
1063 help="Print selected attachment (single PR only)") 1096 nomoreoptions = False
1064 1097
1065 p.add_argument("-r", "--raw", 1098 #
1066 action = 'store_const', const="RAW", 1099 # Functions for the options
1067 dest = 'output', 1100 #
1068 help="Print exactly what the database returns") 1101
1069 p.add_argument("-l", "--list", 1102 def do_paranoid():
1070 action = 'store_const', const="LIST", 1103 nonlocal paranoid, publiconly
1071 dest = 'output', 1104 paranoid = True
1072 help="Print in list form (default)") 1105 publiconly = True
1073 p.add_argument("--headers", 1106
1074 action = 'store_const', const="HEADERS", 1107 def do_help():
1075 dest = 'output', 1108 usage()
1076 help="Print header information only") 1109 exit(0)
1077 p.add_argument("--meta", 1110 def do_version():
1078 action = 'store_const', const="META", 1111 msg = "query-pr {}\n".format(program_version)
1079 dest = 'output', 1112 sys.stdout.write(msg)
1080 help="Print all metadata") 1113 exit(0)
1081 p.add_argument("--metadata", 1114 def do_fields():
1082 action = 'store_const', const="META", 1115 ops.append(Invocation.mkfields())
1083 dest = 'output') 1116 def do_show(field):
1084 p.add_argument("-f", "--full", 1117 ops.append(Invocation.mkshow(field))
1085 action = 'store_const', const="FULL", 1118 def do_range(field):
1086 dest = 'output', 1119 ops.append(Invocation.mkrange(field))
1087 help="Print everything") 1120
1088 1121 def do_search(term):
1089 p.add_argument("--text", 1122 queries.append(Invocation.mkterm(term))
1090 action = 'store_const', const="TEXT", 1123 def do_sql(text):
1091 dest = 'outformat', 1124 assert(not paranoid)
1092 help="Print in text format (default)") 1125 queries.append(Invocation.mksql(text))
1093 p.add_argument("--csv", 1126
1094 action = 'store_const', const="CSV", 1127 def do_open():
1095 dest = 'outformat', 1128 nonlocal openonly
1096 help="Print a CSV file") 1129 openonly = True
1097 p.add_argument("--xml", 1130 def do_closed():
1098 action = 'store_const', const="XML", 1131 nonlocal openonly
1099 dest = 'outformat', 1132 openonly = False
1100 help="Print in XML") 1133 def do_public():
1101 p.add_argument("--json", 1134 nonlocal publiconly
1102 action = 'store_const', const="JSON", 1135 publiconly = True
1103 dest = 'outformat', 1136 def do_privileged():
1104 help="Print in JSON") 1137 nonlocal publiconly
1105 p.add_argument("--rdf", 1138 assert(not paranoid)
1106 action = 'store_const', const="RDF", 1139 publiconly = False
1107 dest = 'outbformat', 1140
1108 help="Print in RDF") 1141 def do_oldest():
1109 p.add_argument("--rdflike", 1142 orders.append(Invocation.mkoldest())
1110 action = 'store_const', const="RDFLIKE", 1143 def do_newest():
1111 dest = 'outformat', 1144 orders.append(Invocation.mknewest())
1112 help="Print RDF-like text") 1145 def do_staleness():
1113 1146 orders.append(Invocation.mkstaleness())
1114 p.add_argument("TERM", nargs='*', 1147 def do_orderby(field):
1115 action=CtorAppend, dest='queries', 1148 orders.append(Invocation.mkfield(field))
1116 const=Invocation.Query.doqstring, 1149 def do_revorderby(field):
1117 help="Search term") 1150 orders.append(Invocation.mkrevfield(field))
1118 1151
1119 args = p.parse_args() 1152 def do_message(n):
1120 1153 selections.append(Invocation.mkmessage(n))
1121 ops = args.ops 1154 def do_attachment(n):
1122 if ops is None: 1155 selections.append(Invocation.mkattachment(n))
1123 ops = [] 1156
1124 queries = args.queries 1157 def do_raw():
1125 if queries is not None: 1158 nonlocal output
1126 openonly = args.openonly 1159 output = "RAW"
1127 if openonly is None: 1160 def do_list():
1128 openonly = True 1161 nonlocal output
1129 publiconly = args.publiconly 1162 output = "LIST"
1130 if publiconly is None: 1163 def do_headers():
1131 publiconly = False 1164 nonlocal output
1132 orders = args.orders 1165 output = "HEADERS"
1133 if orders is None: 1166 def do_meta():
1134 orders = [Invocation.Order.dooldest(None)] 1167 nonlocal output
1135 output = args.output 1168 output = "META"
1136 if output is None: 1169 def do_full():
1137 output = "LIST" 1170 nonlocal output
1138 outformat = args.outformat 1171 output = "FULL"
1139 if outformat is None: 1172
1140 outformat = "TEXT" 1173 def do_text():
1141 selections = args.selections 1174 nonlocal outformat
1142 if selections is None: 1175 outformat = "TEXT"
1143 sel = Invocation.Selection.dopr(output, outformat) 1176 def do_csv():
1144 selections = [sel] 1177 nonlocal outformat
1145 search = Search(queries, openonly, publiconly, orders) 1178 outformat = "CSV"
1146 op = dosearch(search, selections) 1179 def do_xml():
1147 ops.append(op) 1180 nonlocal outformat
1148 # endif 1181 outformat = "XML"
1182 def do_json():
1183 nonlocal outformat
1184 outformat = "JSON"
1185 def do_rdf():
1186 nonlocal outformat
1187 outformat = "RDF"
1188 def do_rdflike():
1189 nonlocal outformat
1190 outformat = "RDFLIKE"
1191
1192 def do_unknown(opt):
1193 sys.stderr.write("Unknown option {}\n".format(opt))
1194 exit(1)
1195
1196 args = ArgHolder(argv)
1197 while True:
1198 arg = args.next()
1199 if arg is None:
1200 break
1201
1202 if nomoreoptions or arg[0] != "-":
1203 do_search(arg)
1204 elif arg == "--":
1205 nomoreoptions = True
1206 # Long options
1207 elif arg == "--attachment":
1208 do_attachment(args.getarg(arg))
1209 elif arg.startswith("--attachment="):
1210 do_message(arg[13:])
1211 elif arg == "--closed":
1212 do_closed()
1213 elif arg == "--csv":
1214 do_csv()
1215 elif arg == "--fields":
1216 do_fields()
1217 elif arg == "--full":
1218 do_full()
1219 elif arg == "--headers":
1220 do_headers()
1221 elif arg == "--help":
1222 do_help()
1223 elif arg == "--json":
1224 do_json()
1225 elif arg == "--list":
1226 do_list()
1227 elif arg == "--message":
1228 do_message(args.getarg(arg))
1229 elif arg.startswith("--message="):
1230 do_message(arg[10:])
1231 elif arg == "--meta":
1232 do_meta()
1233 elif arg == "--metadata":
1234 do_meta()
1235 elif arg == "--newest":
1236 do_newest()
1237 elif arg == "--oldest":
1238 do_oldest()
1239 elif arg == "--orderby":
1240 do_orderby(args.getarg(arg))
1241 elif arg.startswith("--orderby="):
1242 do_orderby(arg[10:])
1243 elif arg == "--open":
1244 do_open()
1245 elif arg == "--paranoid":
1246 do_paranoid()
1247 elif arg == "--public":
1248 do_public()
1249 elif arg == "--privileged" and not paranoid:
1250 do_privileged()
1251 elif arg == "--range":
1252 do_range(args.getarg(arg))
1253 elif arg.startswith("--range="):
1254 do_range(arg[8:])
1255 elif arg == "--raw":
1256 do_raw()
1257 elif arg == "--rdf":
1258 do_rdf()
1259 elif arg == "--rdflike":
1260 do_rdflike()
1261 elif arg == "--revorderby":
1262 do_revorderby(args.getarg(arg))
1263 elif arg.startswith("--revorderby="):
1264 do_revorderby(arg[13:])
1265 elif arg == "--search":
1266 do_search(args.getarg(arg))
1267 elif arg.startswith("--search="):
1268 do_search(arg[9:])
1269 elif arg == "--show":
1270 do_show(args.getarg(arg))
1271 elif arg.startswith("--show="):
1272 do_show(arg[7:])
1273 elif arg == "--sql" and not paranoid:
1274 do_sql(args.getarg(arg))
1275 elif arg.startswith("--sql=") and not paranoid:
1276 do_sql(arg[7:])
1277 elif arg == "--staleness":
1278 do_staleness()
1279 elif arg == "--text":
1280 do_text()
1281 elif arg == "--version":
1282 do_version()
1283 elif arg == "--xml":
1284 do_xml()
1285 elif arg.startswith("--"):
1286 do_unknown(arg)
1287 else:
1288 # short options
1289 i = 1
1290 n = len(arg)
1291 while i < n:
1292 opt = arg[i]
1293 i += 1
1294 def getarg():
1295 nonlocal i
1296 if i < n:
1297 ret = arg[i:]
1298 else:
1299 ret = args.getarg("-" + opt)
1300 i = n
1301 return ret
1302
1303 if opt == "a":
1304 do_attachment(getarg())
1305 elif opt == "f":
1306 do_full()
1307 elif opt == "h":
1308 do_help()
1309 elif opt == "l":
1310 do_list()
1311 elif opt == "m":
1312 do_message(getarg())
1313 elif opt == "s" and not paranoid:
1314 do_sql(getarg())
1315 elif opt == "r":
1316 do_raw()
1317 elif opt == "v":
1318 do_version()
1319 else:
1320 do_unknown("-" + opt)
1321
1322 # Now convert what we got to a single thing.
1323 if queries != []:
1324 if orders == []:
1325 orders = [Invocation.mkoldest()]
1326 if selections == []:
1327 selections = [Invocation.mkpr(output, outformat)]
1328 search = Invocation.Search(queries, openonly, publiconly, orders)
1329 ops.append(Invocation.mksearch(search, selections))
1330 else:
1331 if orders != []:
1332 msg = "No queries given for requested orderings\n"
1333 sys.stderr.write(msg)
1334 exit(1)
1335 if selections != []:
1336 msg = "No queries given for requested selections\n"
1337 sys.stderr.write(msg)
1338 exit(1)
1149 1339
1150 return Invocation(ops) 1340 return Invocation(ops)
1151 # end getargs 1341 # end getargs
1152 1342
1153 ############################################################ 1343 ############################################################
1154 # main 1344 # main
1155 1345
1156 todo = getargs() 1346 todo = getargs(sys.argv)
1347 #todo.dump(Dumper(sys.stdout))
1348
1157 opendb() 1349 opendb()
1158 fetch_classifications() 1350 fetch_classifications()
1159 todo = compile(todo) 1351 todo = compile(todo)
1160 run(todo) 1352 run(todo)
1161 closedb() 1353 closedb()