Commits

larry committed ea0d3c2

Dryparse now supports specifying an option with a value multiple times.
You add a '*' to the option string in the tuple.

Comments (0)

Files changed (1)

 #	* if the item is a string starting with a dash,
 #     it's a whitespace-separated list of command-line
 #     dashed arguments that map to this argument
-#	    * special case: if it contains the string '--' (or '-')
+#	    * special case: if it contains the string '-'
 #         that means use the name of the variable itself
-#		  e.g. foo(abc='--') means it maps to option --abc
+#		  e.g. foo(abc='-') means it maps to option --abc
+#         (if the variable is one letter long it's a single
+#          dash, otherwise it's a double dash)
+#       * another special case: if it contains the string '*',
+#         this is an option that takes a value but you want to
+#         accept it any number of times.
+#         the values you get passed in will be a list containing
+#         all arguments from the command-line in order.
 #       * if unspecified, this is a positional parameter
 #   * if it's a string not starting with a dash,
 #     it's documentation
 
 @all
 class DryArgument:
+	def init_value(self):
+		if self.multiple:
+			self.value = []
+		else:
+			self.value = self.default
+
+	def set_value(self, value):
+		value = self.type(value)
+		if self.multiple:
+			self.value.append(value)
+		else:
+			self.value = value
+
 	def __init__(self, name, default, annotations):
 		self.name = name
 		self.default = default
 		self.type = None
 		self.doc = None
 		self.options = set()
+		self.multiple = False
 		for a in annotations or ():
 			if callable(a):
 				assert self.type is None
 				if a.startswith('-'):
 					# options
 					for option in a.split():
+						if option == '*':
+							assert self.default is unspecified, "You can't specify a default for an option that accepts multiple values. (" + repr(name) + ")"
+							self.default = []
+							self.multiple = True
 						option = option.lstrip('-')
 						if not option:
 							option = name
 			assert None, "unknown annotation for " + name
 
 		if self.type is None:
-			if self.options:
+			if self.options and not self.multiple:
+				# if it's an option, but isn't a multiple option,
 				# if they didn't specify a value for the option,
 				# and they didn't annotate with a type
 				# and it's a option (not a positional argument)
 		needs_value = None
 
 		for a in self.all_arguments:
-			a.value = a.default
+			a.init_value()
 
 		def analyze_option(option):
 			argument = self.options[option]
 				if value is unspecified:
 					needs_value = argument
 				else:
-					argument.value = argument.type(value)
+					argument.set_value(value)
 			else:
 				argument.value = not argument.value
 			seen.add(option)
 				arguments.append(a)
 				continue
 			if needs_value:
-				needs_value.value = needs_value.type(a)
+				needs_value.set_value(a)
 				needs_value = None
 				continue
 			if a == '--':
 		self.assertEqual(self.c.a, 5)
 		self.assertEqual(self.c.args, (6, 7, 8, 9))
 
+	def test_multiple_option(self):
+		self.dump = None
+		def command(self, dump:('- -d *', int)):
+			self.dump = dump
+		self.add(command)
+
+		self.main('command -d 5 -d 6 -d 7')
+		self.assertEqual(self.c.dump, [5, 6, 7])
+
 
 def eval_or_str(s):
 	try:
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.