Merged revisions 61038,61042-61045,61047,61050,61053,61055-61056,61061-61062,61066,61068,61070,61081-61095 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r61081 | neal.norwitz | 2008-02-26 09:04:59 +0100 (Tue, 26 Feb 2008) | 7 lines

  Speed up this test by about 99%.  Remove sleeps and replace with events.
  (This may fail on some slow platforms, but we can fix those cases which
  should be relatively isolated and easier to find now.)
  Move two test cases that didn't require a server to be started
  to a separate TestCase.  These tests were taking 3 seconds which
  is what the timeout was set to.
........
  r61082 | christian.heimes | 2008-02-26 09:18:11 +0100 (Tue, 26 Feb 2008) | 1 line

  The contains function raised a gcc warning. The new code is copied straight from py3k.
........
  r61084 | neal.norwitz | 2008-02-26 09:21:28 +0100 (Tue, 26 Feb 2008) | 3 lines

  Add a timing flag to Trace so you can see where slowness occurs
  like waiting for socket timeouts in test_smtplib :-).
........
  r61086 | christian.heimes | 2008-02-26 18:23:51 +0100 (Tue, 26 Feb 2008) | 3 lines

  Patch #1691070 from Roger Upole: Speed up PyArg_ParseTupleAndKeywords() and improve error msg
  My tests don't show the promised speed up of 10%. The code is as fast as the old code for simple cases and slightly faster for complex cases with several of args and kwargs. But the patch simplifies the code, too.
........
  r61087 | georg.brandl | 2008-02-26 20:13:45 +0100 (Tue, 26 Feb 2008) | 2 lines

  #2194: fix some typos.
........
  r61088 | raymond.hettinger | 2008-02-27 00:40:50 +0100 (Wed, 27 Feb 2008) | 1 line

  Add itertools.combinations().
........
  r61089 | raymond.hettinger | 2008-02-27 02:08:04 +0100 (Wed, 27 Feb 2008) | 1 line

  One too many decrefs.
........
  r61090 | raymond.hettinger | 2008-02-27 02:08:30 +0100 (Wed, 27 Feb 2008) | 1 line

  Larger test range
........
  r61091 | raymond.hettinger | 2008-02-27 02:44:34 +0100 (Wed, 27 Feb 2008) | 1 line

  Simply the sample code for combinations().
........
diff --git a/Python/getargs.c b/Python/getargs.c
index c0b3624..abe0887 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -1401,6 +1401,7 @@
 	return retval;
 }
 
+#define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':')
 
 static int
 vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
@@ -1408,13 +1409,10 @@
 {
 	char msgbuf[512];
 	int levels[32];
-	const char *fname, *message;
-	int min, max;
-	const char *formatsave;
+	const char *fname, *msg, *custom_msg, *keyword;
+	int min = INT_MAX;
 	int i, len, nargs, nkeywords;
-	const char *msg;
-	char **p;
-	PyObject *freelist = NULL;
+	PyObject *freelist = NULL, *current_arg;
 
 	assert(args != NULL && PyTuple_Check(args));
 	assert(keywords == NULL || PyDict_Check(keywords));
@@ -1422,166 +1420,106 @@
 	assert(kwlist != NULL);
 	assert(p_va != NULL);
 
-	/* Search the format:
-	   message <- error msg, if any (else NULL).
-	   fname <- routine name, if any (else NULL).
-	   min <- # of required arguments, or -1 if all are required.
-	   max <- most arguments (required + optional).
-	   Check that kwlist has a non-NULL entry for each arg.
-	   Raise error if a tuple arg spec is found.
-	*/
-	fname = message = NULL;
-	formatsave = format;
-	p = kwlist;
-	min = -1;
-	max = 0;
-	while ((i = *format++) != '\0') {
-		if (isalpha(Py_CHARMASK(i)) && i != 'e') {
-			max++;
-			if (*p == NULL) {
-				PyErr_SetString(PyExc_RuntimeError,
-					"more argument specifiers than "
-					"keyword list entries");
-				return 0;
-			}
-			p++;
-		}
-		else if (i == '|')
-			min = max;
-		else if (i == ':') {
-			fname = format;
-			break;
-		}
-		else if (i == ';') {
-			message = format;
-			break;
-		}
-		else if (i == '(') {
-			PyErr_SetString(PyExc_RuntimeError,
-				"tuple found in format when using keyword "
-				"arguments");
-			return 0;
-		}
+	/* grab the function name or custom error msg first (mutually exclusive) */
+	fname = strchr(format, ':');
+	if (fname) {
+		fname++;
+		custom_msg = NULL;
 	}
-	format = formatsave;
-	if (*p != NULL) {
-		PyErr_SetString(PyExc_RuntimeError,
-			"more keyword list entries than "
-			"argument specifiers");
-		return 0;
+	else {
+		custom_msg = strchr(format,';');
+		if (custom_msg)
+			custom_msg++;
 	}
-	if (min < 0) {
-		/* All arguments are required. */
-		min = max;
-	}
+
+	/* scan kwlist and get greatest possible nbr of args */
+	for (len=0; kwlist[len]; len++)
+		continue;
 
 	nargs = PyTuple_GET_SIZE(args);
-	nkeywords = keywords == NULL ? 0 : PyDict_Size(keywords);
-
-	/* make sure there are no duplicate values for an argument;
-	   its not clear when to use the term "keyword argument vs.
-	   keyword parameter in messages */
-	if (nkeywords > 0) {
-		for (i = 0; i < nargs; i++) {
-			const char *thiskw = kwlist[i];
-			if (thiskw == NULL)
-				break;
-			if (PyDict_GetItemString(keywords, thiskw)) {
-				PyErr_Format(PyExc_TypeError,
-					"keyword parameter '%s' was given "
-					"by position and by name",
-					thiskw);
-				return 0;
-			}
-			else if (PyErr_Occurred())
-				return 0;
-		}
-	}
-
-	/* required arguments missing from args can be supplied by keyword
-	   arguments; set len to the number of positional arguments, and,
-	   if that's less than the minimum required, add in the number of
-	   required arguments that are supplied by keywords */
-	len = nargs;
-	if (nkeywords > 0 && nargs < min) {
-		for (i = nargs; i < min; i++) {
-			if (PyDict_GetItemString(keywords, kwlist[i]))
-				len++;
-			else if (PyErr_Occurred())
-				return 0;
-		}
-	}
-
-	/* make sure we got an acceptable number of arguments; the message
-	   is a little confusing with keywords since keyword arguments
-	   which are supplied, but don't match the required arguments
-	   are not included in the "%d given" part of the message
-	   XXX and this isn't a bug!? */
-	if (len < min || max < len) {
-		if (message == NULL) {
-			PyOS_snprintf(msgbuf, sizeof(msgbuf),
-				      "%.200s%s takes %s %d argument%s "
-				      "(%d given)",
-				      fname==NULL ? "function" : fname,
-				      fname==NULL ? "" : "()",
-				      min==max ? "exactly"
-			              : len < min ? "at least" : "at most",
-				      len < min ? min : max,
-				      (len < min ? min : max) == 1 ? "" : "s",
-				      len);
-			message = msgbuf;
-		}
-		PyErr_SetString(PyExc_TypeError, message);
+	nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords);
+	if (nargs + nkeywords > len) {
+		PyErr_Format(PyExc_TypeError, "%s%s takes at most %d "
+			     "argument%s (%d given)",
+			     (fname == NULL) ? "function" : fname,
+			     (fname == NULL) ? "" : "()",
+			     len,
+			     (len == 1) ? "" : "s",
+			     nargs + nkeywords);
 		return 0;
 	}
 
-	/* convert the positional arguments */
-	for (i = 0; i < nargs; i++) {
-		if (*format == '|')
+	/* convert tuple args and keyword args in same loop, using kwlist to drive process */
+	for (i = 0; i < len; i++) {
+		keyword = kwlist[i];
+		if (*format == '|') {
+			min = i;
 			format++;
-		msg = convertitem(PyTuple_GET_ITEM(args, i), &format, p_va,
-				  flags, levels, msgbuf, sizeof(msgbuf),
-				  &freelist);
+		}
+		if (IS_END_OF_FORMAT(*format)) {
+			PyErr_Format(PyExc_RuntimeError,
+				     "More keyword list entries (%d) than "
+				     "format specifiers (%d)", len, i);
+			return cleanreturn(0, freelist);
+		}
+		current_arg = NULL;
+		if (nkeywords) {
+			current_arg = PyDict_GetItemString(keywords, keyword);
+		}
+		if (current_arg) {
+			--nkeywords;
+			if (i < nargs) {
+				/* arg present in tuple and in dict */
+				PyErr_Format(PyExc_TypeError,
+					     "Argument given by name ('%s') "
+					     "and position (%d)",
+					     keyword, i+1);
+				return cleanreturn(0, freelist);
+			}
+		}
+		else if (nkeywords && PyErr_Occurred())
+			return cleanreturn(0, freelist);
+		else if (i < nargs)
+			current_arg = PyTuple_GET_ITEM(args, i);
+			
+		if (current_arg) {
+			msg = convertitem(current_arg, &format, p_va, flags,
+				levels, msgbuf, sizeof(msgbuf), &freelist);
+			if (msg) {
+				seterror(i+1, msg, levels, fname, custom_msg);
+				return cleanreturn(0, freelist);
+			}
+			continue;
+		}
+
+		if (i < min) {
+			PyErr_Format(PyExc_TypeError, "Required argument "
+				     "'%s' (pos %d) not found",
+				     keyword, i+1);
+			return cleanreturn(0, freelist);
+		}
+		/* current code reports success when all required args
+		 * fulfilled and no keyword args left, with no further
+		 * validation. XXX Maybe skip this in debug build ?
+		 */
+		if (!nkeywords)
+			return cleanreturn(1, freelist);
+
+		/* We are into optional args, skip thru to any remaining
+		 * keyword args */
+		msg = skipitem(&format, p_va, flags);
 		if (msg) {
-			seterror(i+1, msg, levels, fname, message);
+			PyErr_Format(PyExc_RuntimeError, "%s: '%s'", msg,
+				     format);
 			return cleanreturn(0, freelist);
 		}
 	}
 
-	/* handle no keyword parameters in call */
-	if (nkeywords == 0)
-		return cleanreturn(1, freelist);
-
-	/* convert the keyword arguments; this uses the format
-	   string where it was left after processing args */
-	for (i = nargs; i < max; i++) {
-		PyObject *item;
-		if (*format == '|')
-			format++;
-		item = PyDict_GetItemString(keywords, kwlist[i]);
-		if (item != NULL) {
-			Py_INCREF(item);
-			msg = convertitem(item, &format, p_va, flags, levels,
-					  msgbuf, sizeof(msgbuf), &freelist);
-			Py_DECREF(item);
-			if (msg) {
-				seterror(i+1, msg, levels, fname, message);
-				return cleanreturn(0, freelist);
-			}
-			--nkeywords;
-			if (nkeywords == 0)
-				break;
-		}
-		else if (PyErr_Occurred())
-			return cleanreturn(0, freelist);
-		else {
-			msg = skipitem(&format, p_va, flags);
-			if (msg) {
-				levels[0] = 0;
-				seterror(i+1, msg, levels, fname, message);
-				return cleanreturn(0, freelist);
-			}
-		}
+	if (!IS_END_OF_FORMAT(*format)) {
+		PyErr_Format(PyExc_RuntimeError,
+			"more argument specifiers than keyword list entries "
+			"(remaining format:'%s')", format);
+		return cleanreturn(0, freelist);
 	}
 
 	/* make sure there are no extraneous keyword arguments */
@@ -1597,7 +1535,7 @@
 				return cleanreturn(0, freelist);
 			}
 			ks = PyUnicode_AsString(key);
-			for (i = 0; i < max; i++) {
+			for (i = 0; i < len; i++) {
 				if (!strcmp(ks, kwlist[i])) {
 					match = 1;
 					break;
@@ -1620,7 +1558,7 @@
 static char *
 skipitem(const char **p_format, va_list *p_va, int flags)
 {
-        const char *format = *p_format;
+	const char *format = *p_format;
 	char c = *format++;
 
 	switch (c) {
@@ -1726,15 +1664,32 @@
 			break;
 		}
 
+	case '(':	/* bypass tuple, not handled at all previously */
+		{
+			char *msg;
+			for (;;) {
+				if (*format==')')
+					break;
+				if (IS_END_OF_FORMAT(*format))
+					return "Unmatched left paren in format "
+					       "string";
+				msg = skipitem(&format, p_va, flags);
+				if (msg)
+					return msg;
+			}
+			format++;
+			break;
+		}
+
+	case ')':
+		return "Unmatched right paren in format string";
+
 	default:
 err:
 		return "impossible<bad format char>";
 
 	}
 
-	/* The "(...)" format code for tuples is not handled here because
-	 * it is not allowed with keyword args. */
-
 	*p_format = format;
 	return NULL;
 }