[Python-3000-checkins] r57526 - in python/branches/py3k: Include/unicodeobject.h Lib/string.py Lib/test/test_string.py Lib/test/test_unicode.py Objects/stringlib/string_format.h Objects/unicodeobject.c Python/sysmodule.c

eric.smith python-3000-checkins at python.org
Mon Aug 27 00:27:14 CEST 2007


Author: eric.smith
Date: Mon Aug 27 00:27:13 2007
New Revision: 57526

Modified:
   python/branches/py3k/Include/unicodeobject.h
   python/branches/py3k/Lib/string.py
   python/branches/py3k/Lib/test/test_string.py
   python/branches/py3k/Lib/test/test_unicode.py
   python/branches/py3k/Objects/stringlib/string_format.h
   python/branches/py3k/Objects/unicodeobject.c
   python/branches/py3k/Python/sysmodule.c
Log:
PEP 3101: Completed string.Formatter class.  Reimplemented field_name to object transformation.

Modified: python/branches/py3k/Include/unicodeobject.h
==============================================================================
--- python/branches/py3k/Include/unicodeobject.h	(original)
+++ python/branches/py3k/Include/unicodeobject.h	Mon Aug 27 00:27:13 2007
@@ -1440,8 +1440,7 @@
 PyObject *
 _unicodeformatter_iterator(PyObject *str);
 PyObject *
-_unicodeformatter_lookup(PyObject *field_name, PyObject *args,
-                         PyObject *kwargs);
+_unicodeformatter_field_name_split(PyObject *field_name);
 
 #ifdef __cplusplus
 }

Modified: python/branches/py3k/Lib/string.py
==============================================================================
--- python/branches/py3k/Lib/string.py	(original)
+++ python/branches/py3k/Lib/string.py	Mon Aug 27 00:27:13 2007
@@ -200,31 +200,59 @@
 # exposed here via the sys module.  sys was chosen because it's always
 # available and doesn't have to be dynamically loaded.
 
-# The parser is implemented in sys._formatter_parser.
-# The "object lookup" is implemented in sys._formatter_lookup
+# The overall parser is implemented in sys._formatter_parser.
+# The field name parser is implemented in sys._formatter_field_name_split
 
-from sys import _formatter_parser, _formatter_lookup
+from sys import _formatter_parser, _formatter_field_name_split
 
 class Formatter:
     def format(self, format_string, *args, **kwargs):
         return self.vformat(format_string, args, kwargs)
 
     def vformat(self, format_string, args, kwargs):
+        used_args = set()
         result = []
         for (is_markup, literal, field_name, format_spec, conversion) in \
                 _formatter_parser(format_string):
             if is_markup:
-                # find the object
-                index, name, obj = _formatter_lookup(field_name, args, kwargs)
+                # given the field_name, find the object it references
+
+                # split it into the first part, and and iterator that
+                #  looks over the rest
+                first, rest = _formatter_field_name_split(field_name)
+
+                used_args.add(first)
+                obj = self.get_value(first, args, kwargs)
+
+                # loop through the rest of the field_name, doing
+                #  getattr or getitem as needed
+                for is_attr, i in rest:
+                    if is_attr:
+                        obj = getattr(obj, i)
+                    else:
+                        obj = obj[i]
+
+                # do any conversion on the resulting object
+                if conversion == 'r':
+                    obj = repr(obj)
+                elif conversion == 's':
+                    obj = str(obj)
+
+                # format the object and append to the result
+                result.append(self.format_field(obj, format_spec))
             else:
                 result.append(literal)
+        self.check_unused_args(used_args, args, kwargs)
         return ''.join(result)
 
     def get_value(self, key, args, kwargs):
-        pass
+        if isinstance(key, int):
+            return args[key]
+        else:
+            return kwargs[key]
 
     def check_unused_args(self, used_args, args, kwargs):
         pass
 
     def format_field(self, value, format_spec):
-        pass
+        return format(value, format_spec)

Modified: python/branches/py3k/Lib/test/test_string.py
==============================================================================
--- python/branches/py3k/Lib/test/test_string.py	(original)
+++ python/branches/py3k/Lib/test/test_string.py	Mon Aug 27 00:27:13 2007
@@ -19,8 +19,27 @@
         fmt = string.Formatter()
         self.assertEqual(fmt.format("foo"), "foo")
 
-        # Formatter not working you for lookups
-        #self.assertEqual(fmt.format("foo{0}", "bar"), "foobar")
+        self.assertEqual(fmt.format("foo{0}", "bar"), "foobar")
+        self.assertEqual(fmt.format("foo{1}{0}-{1}", "bar", 6), "foo6bar-6")
+        self.assertEqual(fmt.format("-{arg!r}-", arg='test'), "-'test'-")
+
+        class NamespaceFormatter(string.Formatter):
+            def __init__(self, namespace={}):
+                string.Formatter.__init__(self)
+                self.namespace = namespace
+
+            def get_value(self, key, args, kwds):
+                if isinstance(key, str):
+                    try:
+                        # Check explicitly passed arguments first
+                        return kwds[key]
+                    except KeyError:
+                        return self.namespace[key]
+                else:
+                    string.Formatter.get_value(key, args, kwds)
+
+        fmt = NamespaceFormatter({'greeting':'hello'})
+        self.assertEqual(fmt.format("{greeting}, world!"), 'hello, world!')
 
 
     def test_maketrans(self):

Modified: python/branches/py3k/Lib/test/test_unicode.py
==============================================================================
--- python/branches/py3k/Lib/test/test_unicode.py	(original)
+++ python/branches/py3k/Lib/test/test_unicode.py	Mon Aug 27 00:27:13 2007
@@ -449,6 +449,10 @@
         self.assertEqual('}}{{'.format(), '}{')
         self.assertEqual('}}x{{'.format(), '}x{')
 
+        # weird field names
+        self.assertEqual("{0[foo-bar]}".format({'foo-bar':'baz'}), 'baz')
+        self.assertEqual("{0[foo bar]}".format({'foo bar':'baz'}), 'baz')
+
         self.assertEqual('{foo._x}'.format(foo=C(20)), '20')
         self.assertEqual('{1}{0}'.format(D(10), D(20)), '2010')
         self.assertEqual('{0._x.x}'.format(C(D('abc'))), 'abc')
@@ -539,7 +543,11 @@
         self.assertRaises(ValueError, "}".format)
         self.assertRaises(ValueError, "abc{0:{}".format)
         self.assertRaises(ValueError, "{0".format)
+        self.assertRaises(ValueError, "{0.}".format)
+        self.assertRaises(ValueError, "{0[}".format)
+        self.assertRaises(ValueError, "{0]}".format)
         self.assertRaises(ValueError, "{0.[]}".format)
+        self.assertRaises(ValueError, "{0..foo}".format, 0)
         self.assertRaises(ValueError, "{0[0}".format)
         self.assertRaises(ValueError, "{0[0:foo}".format)
         self.assertRaises(ValueError, "{c]}".format)
@@ -551,6 +559,7 @@
         self.assertRaises(ValueError, "{0!rs}".format)
         self.assertRaises(ValueError, "{!}".format)
         self.assertRaises(ValueError, "{:}".format)
+        self.assertRaises(ValueError, "{:s}".format)
         self.assertRaises(ValueError, "{}".format)
 
         # can't have a replacement on the field name portion

Modified: python/branches/py3k/Objects/stringlib/string_format.h
==============================================================================
--- python/branches/py3k/Objects/stringlib/string_format.h	(original)
+++ python/branches/py3k/Objects/stringlib/string_format.h	Mon Aug 27 00:27:13 2007
@@ -72,23 +72,6 @@
     return PyErr_Format(PyExc_ValueError, "%s in format string", s);
 }
 
-/*
-    check_input returns True if we still have characters
-    left in the input string.
-
-    XXX: make this function go away when better error handling is
-    implemented.
-*/
-Py_LOCAL_INLINE(int)
-check_input(SubString *input)
-{
-    if (input->ptr < input->end)
-        return 1;
-    PyErr_SetString(PyExc_ValueError,
-                    "unterminated replacement field");
-    return 0;
-}
-
 /************************************************************************/
 /***********    Output string management functions       ****************/
 /************************************************************************/
@@ -161,46 +144,22 @@
 /***********  Format string parsing -- integers and identifiers *********/
 /************************************************************************/
 
-/*
-    end_identifier returns true if a character marks
-    the end of an identifier string.
-
-    Although the PEP specifies that identifiers are
-    numbers or valid Python identifiers, we just let
-    getattr/getitem handle that, so the implementation
-    is more flexible than the PEP would indicate.
-*/
-Py_LOCAL_INLINE(int)
-end_identifier(STRINGLIB_CHAR c)
+static Py_ssize_t
+get_integer(const SubString *str)
 {
-    switch (c) {
-    case '.': case '[': case ']':
-        return 1;
-    default:
-        return 0;
-    }
-}
-
-/*
-    get_integer consumes 0 or more decimal digit characters from an
-    input string, updates *result with the corresponding positive
-    integer, and returns the number of digits consumed.
+    Py_ssize_t accumulator = 0;
+    Py_ssize_t digitval;
+    Py_ssize_t oldaccumulator;
+    STRINGLIB_CHAR *p;
+
+    /* empty string is an error */
+    if (str->ptr >= str->end)
+        return -1;
 
-    returns -1 on error.
-*/
-static int
-get_integer(STRINGLIB_CHAR **ptr, STRINGLIB_CHAR *end,
-                  Py_ssize_t *result)
-{
-    Py_ssize_t accumulator, digitval, oldaccumulator;
-    int numdigits;
-    accumulator = numdigits = 0;
-    for (;;(*ptr)++, numdigits++) {
-        if (*ptr >= end)
-            break;
-        digitval = STRINGLIB_TODECIMAL(**ptr);
+    for (p = str->ptr; p < str->end; p++) {
+        digitval = STRINGLIB_TODECIMAL(*p);
         if (digitval < 0)
-            break;
+            return -1;
         /*
            This trick was copied from old Unicode format code.  It's cute,
            but would really suck on an old machine with a slow divide
@@ -216,70 +175,215 @@
         }
         accumulator += digitval;
     }
-    *result = accumulator;
-    return numdigits;
+    return accumulator;
 }
 
-/*
-    get_identifier is a bit of a misnomer.  It returns a value for use
-    with getattr or getindex.  This value will a string/unicode
-    object. The input cannot be zero length.  Continues until end of
-    input, or end_identifier() returns true.
-*/
+/************************************************************************/
+/******** Functions to get field objects and specification strings ******/
+/************************************************************************/
+
+/* do the equivalent of obj.name */
 static PyObject *
-get_identifier(SubString *input)
+getattr(PyObject *obj, SubString *name)
 {
-    STRINGLIB_CHAR *start;
+    PyObject *newobj;
+    PyObject *str = STRINGLIB_NEW(name->ptr, name->end - name->ptr);
+    if (str == NULL)
+        return NULL;
+    newobj = PyObject_GetAttr(obj, str);
+    Py_DECREF(str);
+    return newobj;
+}
+
+/* do the equivalent of obj[idx], where obj is a sequence */
+static PyObject *
+getitem_sequence(PyObject *obj, Py_ssize_t idx)
+{
+    return PySequence_GetItem(obj, idx);
+}
 
-    for (start = input->ptr;
-         input->ptr < input->end && !end_identifier(*input->ptr);
-         input->ptr++)
-        ;
-
-    return  STRINGLIB_NEW(start, input->ptr - start);
-
-    /*
-        We might want to add code here to check for invalid Python
-        identifiers.  All identifiers are eventually passed to getattr
-        or getitem, so there is a check when used.  However, we might
-        want to remove (or not) the ability to have strings like
-        "a/b" or " ab" or "-1" (which is not parsed as a number).
-        For now, this is left as an exercise for the first disgruntled
-        user...
+/* do the equivalent of obj[idx], where obj is not a sequence */
+static PyObject *
+getitem_idx(PyObject *obj, Py_ssize_t idx)
+{
+    PyObject *newobj;
+    PyObject *idx_obj = PyInt_FromSsize_t(idx);
+    if (idx_obj == NULL)
+        return NULL;
+    newobj = PyObject_GetItem(obj, idx_obj);
+    Py_DECREF(idx_obj);
+    return newobj;
+}
 
-    if (XXX -- need check function) {
-        Py_DECREF(result);
-        PyErr_SetString(PyExc_ValueError,
-                      "Invalid embedded Python identifier");
+/* do the equivalent of obj[name] */
+static PyObject *
+getitem_str(PyObject *obj, SubString *name)
+{
+    PyObject *newobj;
+    PyObject *str = STRINGLIB_NEW(name->ptr, name->end - name->ptr);
+    if (str == NULL)
         return NULL;
+    newobj = PyObject_GetItem(obj, str);
+    Py_DECREF(str);
+    return newobj;
+}
+
+typedef struct {
+    /* the entire string we're parsing.  we assume that someone else
+       is managing its lifetime, and that it will exist for the
+       lifetime of the iterator.  can be empty */
+    SubString str;
+
+    /* pointer to where we are inside field_name */
+    STRINGLIB_CHAR *ptr;
+} FieldNameIterator;
+
+
+static int
+FieldNameIterator_init(FieldNameIterator *self, STRINGLIB_CHAR *ptr,
+                       Py_ssize_t len)
+{
+    SubString_init(&self->str, ptr, len);
+    self->ptr = self->str.ptr;
+    return 1;
+}
+
+static int
+_FieldNameIterator_attr(FieldNameIterator *self, SubString *name)
+{
+    STRINGLIB_CHAR c;
+
+    name->ptr = self->ptr;
+
+    /* return everything until '.' or '[' */
+    while (self->ptr < self->str.end) {
+        switch (c = *self->ptr++) {
+        case '[':
+        case '.':
+            /* backup so that we this character will be seen next time */
+            self->ptr--;
+            break;
+        default:
+            continue;
+        }
+        break;
     }
-    */
+    /* end of string is okay */
+    name->end = self->ptr;
+    return 1;
 }
 
-/************************************************************************/
-/******** Functions to get field objects and specification strings ******/
-/************************************************************************/
+static int
+_FieldNameIterator_item(FieldNameIterator *self, SubString *name)
+{
+    STRINGLIB_CHAR c;
 
-/* get_field_and_spec is the main function in this section.  It parses
-   the format string well enough to return a field object to render along
-   with a field specification string.
-*/
+    name->ptr = self->ptr;
 
-/*
-    look up key in our keyword arguments
+    /* return everything until ']' */
+    while (self->ptr < self->str.end) {
+        switch (c = *self->ptr++) {
+        case ']':
+            break;
+        default:
+            continue;
+        }
+        break;
+    }
+    /* end of string is okay */
+    /* don't include the ']' */
+    name->end = self->ptr-1;
+    return 1;
+}
+
+/* returns 0 on error, 1 on non-error termination, and 2 if it returns a value */
+static int
+FieldNameIterator_next(FieldNameIterator *self, int *is_attribute,
+                       Py_ssize_t *name_idx, SubString *name)
+{
+    /* check at end of input */
+    if (self->ptr >= self->str.end)
+        return 1;
+
+    switch (*self->ptr++) {
+    case '.':
+        *is_attribute = 1;
+        if (_FieldNameIterator_attr(self, name) == 0) {
+            return 0;
+        }
+        *name_idx = -1;
+        break;
+    case '[':
+        *is_attribute = 0;
+        if (_FieldNameIterator_item(self, name) == 0) {
+            return 0;
+        }
+        *name_idx = get_integer(name);
+        break;
+    default:
+        /* interal error, can't get here */
+        assert(0);
+        return 0;
+    }
+
+    /* empty string is an error */
+    if (name->ptr == name->end) {
+        PyErr_SetString(PyExc_ValueError, "Empty attribute in format string");
+        return 0;
+    }
+
+    return 2;
+}
+
+
+/* input: field_name
+   output: 'first' points to the part before the first '[' or '.'
+           'first_idx' is -1 if 'first' is not an integer, otherwise
+                       it's the value of first converted to an integer
+           'rest' is an iterator to return the rest
 */
-static PyObject *
-key_lookup(PyObject *kwargs, PyObject *key)
+static int
+field_name_split(STRINGLIB_CHAR *ptr, Py_ssize_t len, SubString *first,
+                 Py_ssize_t *first_idx, FieldNameIterator *rest)
 {
-    PyObject *result;
+    STRINGLIB_CHAR c;
+    STRINGLIB_CHAR *p = ptr;
+    STRINGLIB_CHAR *end = ptr + len;
+
+    /* find the part up until the first '.' or '[' */
+    while (p < end) {
+        switch (c = *p++) {
+        case '[':
+        case '.':
+            /* backup so that we this character is available to the
+               "rest" iterator */
+            p--;
+            break;
+        default:
+            continue;
+        }
+        break;
+    }
 
-    if (kwargs && (result = PyDict_GetItem(kwargs, key)) != NULL) {
-        Py_INCREF(result);
-        return result;
+    /* set up the return values */
+    SubString_init(first, ptr, p - ptr);
+    FieldNameIterator_init(rest, p, end - p);
+
+    /* see if "first" is an integer, in which case it's used as an index */
+    *first_idx = get_integer(first);
+
+    /* zero length string is an error */
+    if (first->ptr >= first->end) {
+        PyErr_SetString(PyExc_ValueError, "empty field name");
+        goto error;
     }
-    return NULL;
+
+    return 1;
+error:
+    return 0;
 }
 
+
 /*
     get_field_object returns the object inside {}, before the
     format_spec.  It handles getindex and getattr lookups and consumes
@@ -288,80 +392,71 @@
 static PyObject *
 get_field_object(SubString *input, PyObject *args, PyObject *kwargs)
 {
-    PyObject *myobj, *subobj, *newobj;
-    STRINGLIB_CHAR c;
+    PyObject *obj = NULL;
+    int ok;
+    int is_attribute;
+    SubString name;
+    SubString first;
     Py_ssize_t index;
-    int isindex, isnumeric, isargument;
-
-    index = isnumeric = 0;  /* Just to shut up the compiler warnings */
-
-    myobj = args;
-    Py_INCREF(myobj);
+    FieldNameIterator rest;
 
-    for (isindex=1, isargument=1;;) {
-        if (!check_input(input))
-            break;
-        if (!isindex) {
-            if ((subobj = get_identifier(input)) == NULL)
-                break;
-            newobj = PyObject_GetAttr(myobj, subobj);
-            Py_DECREF(subobj);
-        } else {
-            isnumeric = (STRINGLIB_ISDECIMAL(*input->ptr));
-            if (isnumeric)
-                /* XXX: add error checking */
-                get_integer(&input->ptr, input->end, &index);
-
-            if (isnumeric && PySequence_Check(myobj))
-                newobj = PySequence_GetItem(myobj, index);
-            else {
-                /* XXX -- do we need PyLong_FromLongLong?
-                                   Using ssizet, not int... */
-                subobj = isnumeric ?
-                          PyInt_FromLong(index) :
-                          get_identifier(input);
-                if (subobj == NULL)
-                    break;
-                if (isargument) {
-                    newobj = key_lookup(kwargs, subobj);
-                } else {
-                    newobj = PyObject_GetItem(myobj, subobj);
-                }
-                Py_DECREF(subobj);
-            }
+    if (!field_name_split(input->ptr, input->end - input->ptr, &first,
+                          &index, &rest)) {
+        goto error;
+    }
+
+    if (index == -1) {
+        /* look up in kwargs */
+        PyObject *key = STRINGLIB_NEW(first.ptr, first.end - first.ptr);
+        if (key == NULL)
+            goto error;
+        if ((kwargs == NULL) || (obj = PyDict_GetItem(kwargs, key)) == NULL) {
+            PyErr_SetString(PyExc_ValueError, "Keyword argument not found "
+                            "in format string");
+            Py_DECREF(key);
+            goto error;
+        }
+    } else {
+        /* look up in args */
+        obj = PySequence_GetItem(args, index);
+        if (obj == NULL) {
+            /* translate IndexError to a ValueError */
+            PyErr_SetString(PyExc_ValueError, "Not enough positional arguments "
+                            "in format string");
+            goto error;
         }
-        Py_DECREF(myobj);
-        myobj = newobj;
-        if (myobj == NULL)
-            break;
-        if (!isargument && isindex)
-            if  ((!check_input(input)) || (*(input->ptr++) != ']')) {
-                SetError("Expected ]");
-                break;
-            }
-
-        /* if at the end of input, return with myobj */
-        if (input->ptr >= input->end)
-            return myobj;
-
-        c = *input->ptr;
-        input->ptr++;
-        isargument = 0;
-        isindex = (c == '[');
-        if (!isindex && (c != '.')) {
-           SetError("Expected ., [, :, !, or }");
-           break;
-        }
-    }
-    if ((myobj == NULL) && isargument) {
-        /* XXX: include more useful error information, like which
-         * keyword not found or which index missing */
-       PyErr_Clear();
-       return SetError(isnumeric
-            ? "Not enough positional arguments"
-            : "Keyword argument not found");
     }
-    Py_XDECREF(myobj);
+
+    /* iterate over the rest of the field_name */
+    while ((ok = FieldNameIterator_next(&rest, &is_attribute, &index,
+                                        &name)) == 2) {
+        PyObject *tmp;
+
+        if (is_attribute)
+            /* getattr lookup "." */
+            tmp = getattr(obj, &name);
+        else
+            /* getitem lookup "[]" */
+            if (index == -1)
+                tmp = getitem_str(obj, &name);
+            else
+                if (PySequence_Check(obj))
+                    tmp = getitem_sequence(obj, index);
+                else
+                    /* not a sequence */
+                    tmp = getitem_idx(obj, index);
+        if (tmp == NULL)
+            goto error;
+
+        /* assign to obj */
+        Py_DECREF(obj);
+        obj = tmp;
+    }
+    /* end of iterator, this is the non-error case */
+    if (ok == 1)
+        return obj;
+error:
+    Py_XDECREF(obj);
     return NULL;
 }
 

Modified: python/branches/py3k/Objects/unicodeobject.c
==============================================================================
--- python/branches/py3k/Objects/unicodeobject.c	(original)
+++ python/branches/py3k/Objects/unicodeobject.c	Mon Aug 27 00:27:13 2007
@@ -9161,9 +9161,8 @@
 static void
 formatteriter_dealloc(formatteriterobject *it)
 {
-	_PyObject_GC_UNTRACK(it);
-	Py_XDECREF(it->str);
-	PyObject_GC_Del(it);
+        Py_XDECREF(it->str);
+	PyObject_FREE(it);
 }
 
 /* returns a tuple:
@@ -9313,7 +9312,7 @@
 {
         formatteriterobject *it;
 
-	it = PyObject_GC_New(formatteriterobject, &PyFormatterIter_Type);
+	it = PyObject_New(formatteriterobject, &PyFormatterIter_Type);
 	if (it == NULL)
 		return NULL;
 
@@ -9326,17 +9325,167 @@
                             PyUnicode_AS_UNICODE(str),
                             PyUnicode_GET_SIZE(str));
 
-	_PyObject_GC_TRACK(it);
 	return (PyObject *)it;
 }
 
-PyObject *
-_unicodeformatter_lookup(PyObject *field_name, PyObject *args,
-                         PyObject *kwargs)
+/********************* FieldName Iterator ************************/
+
+/* this is used to implement string.Formatter.vparse().  it parses
+   the field name into attribute and item values. */
+
+typedef struct {
+	PyObject_HEAD
+
+        /* we know this to be a unicode object, but since we just keep
+           it around to keep the object alive, having it as PyObject
+           is okay */
+        PyObject *str;
+
+        FieldNameIterator it_field;
+} fieldnameiterobject;
+
+static void
+fieldnameiter_dealloc(fieldnameiterobject *it)
+{
+        Py_XDECREF(it->str);
+	PyObject_FREE(it);
+}
+
+/* returns a tuple:
+   (is_attr, value)
+   is_attr is true if we used attribute syntax (e.g., '.foo')
+              false if we used index syntax (e.g., '[foo]')
+   value is an integer or string
+*/
+static PyObject *
+fieldnameiter_next(fieldnameiterobject *it)
 {
+        int result;
+        int is_attr;
+        Py_ssize_t idx;
+        SubString name;
+
+        result = FieldNameIterator_next(&it->it_field, &is_attr,
+                                            &idx, &name);
+        if (result == 0 || result == 1) {
+                /* if 0, error has already been set, if 1, iterator is empty */
+                return NULL;
+        } else {
+                PyObject* result = NULL;
+                PyObject* is_attr_obj = NULL;
+                PyObject* obj = NULL;
+
+                is_attr_obj = PyBool_FromLong(is_attr);
+                if (is_attr_obj == NULL)
+                        goto error;
+
+                /* either an integer or a string */
+                if (idx != -1)
+                        obj = PyInt_FromSsize_t(idx);
+                else
+                        obj = STRINGLIB_NEW(name.ptr, name.end - name.ptr);
+                if (obj == NULL)
+                        goto error;
+
+               /* return a tuple of values */
+                result = PyTuple_Pack(2, is_attr_obj, obj);
+                if (result == NULL)
+                        goto error;
+
+                return result;
+
+        error:
+                Py_XDECREF(result);
+                Py_XDECREF(is_attr_obj);
+                Py_XDECREF(obj);
+                return NULL;
+        }
         return NULL;
 }
 
+static PyMethodDef fieldnameiter_methods[] = {
+ 	{NULL,		NULL}		/* sentinel */
+};
+
+static PyTypeObject PyFieldNameIter_Type = {
+	PyVarObject_HEAD_INIT(&PyType_Type, 0)
+	"fieldnameiterator",			/* tp_name */
+	sizeof(fieldnameiterobject),		/* tp_basicsize */
+	0,					/* tp_itemsize */
+	/* methods */
+	(destructor)fieldnameiter_dealloc,	/* tp_dealloc */
+	0,					/* tp_print */
+	0,					/* tp_getattr */
+	0,					/* tp_setattr */
+	0,					/* tp_compare */
+	0,					/* tp_repr */
+	0,					/* tp_as_number */
+	0,					/* tp_as_sequence */
+	0,					/* tp_as_mapping */
+	0,					/* tp_hash */
+	0,					/* tp_call */
+	0,					/* tp_str */
+	PyObject_GenericGetAttr,		/* tp_getattro */
+	0,					/* tp_setattro */
+	0,					/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT,			/* tp_flags */
+	0,					/* tp_doc */
+	0,					/* tp_traverse */
+	0,					/* tp_clear */
+	0,					/* tp_richcompare */
+	0,					/* tp_weaklistoffset */
+	PyObject_SelfIter,			/* tp_iter */
+	(iternextfunc)fieldnameiter_next,	/* tp_iternext */
+	fieldnameiter_methods,			/* tp_methods */
+        0};
+
+PyObject *
+_unicodeformatter_field_name_split(PyObject *field_name)
+{
+        SubString first;
+        Py_ssize_t first_idx;
+        fieldnameiterobject *it;
+
+        PyObject *first_obj = NULL;
+        PyObject *it_obj = NULL;
+        PyObject *result;
+
+        it = PyObject_New(fieldnameiterobject, &PyFieldNameIter_Type);
+        if (it == NULL)
+                goto error;
+        it->str = NULL;
+        it_obj = (PyObject *)it;
+
+        if (!field_name_split(STRINGLIB_STR(field_name),
+                              STRINGLIB_LEN(field_name),
+                              &first, &first_idx, &it->it_field))
+                goto error;
+
+        /* first becomes an integer, if possible, else a string */
+        if (first_idx != -1)
+                first_obj = PyInt_FromSsize_t(first_idx);
+        else
+                /* convert "first" into a string object */
+                first_obj = STRINGLIB_NEW(first.ptr, first.end - first.ptr);
+        if (first_obj == NULL)
+                goto error;
+
+        /* take ownership, give the object to the iterator.  this is
+           just to keep the field_name alive */
+        Py_INCREF(field_name);
+        it->str = field_name;
+
+        /* return a tuple of values */
+        result = PyTuple_Pack(2, first_obj, it_obj);
+        if (result == NULL)
+                goto error;
+
+        return result;
+error:
+        Py_XDECREF(it_obj);
+        Py_XDECREF(first_obj);
+        return NULL;
+}
 
 /********************* Unicode Iterator **************************/
 

Modified: python/branches/py3k/Python/sysmodule.c
==============================================================================
--- python/branches/py3k/Python/sysmodule.c	(original)
+++ python/branches/py3k/Python/sysmodule.c	Mon Aug 27 00:27:13 2007
@@ -683,28 +683,28 @@
         return _unicodeformatter_iterator(str);
 }
 
-/* sys_formatter_lookup is used to implement string.Formatter.vformat.
-   it takes an PEP 3101 "field name", args, and kwargs, and returns a
-   tuple (index, name, object).  see unicodeobject.c's
-   _unicodeformatter_lookup for details */
+/* sys_formatter_field_name_split is used to implement
+   string.Formatter.vformat.  it takes an PEP 3101 "field name", and
+   returns a tuple of (first, rest): "first", the part before the
+   first '.' or '['; and "rest", an iterator for the rest of the field
+   name.  see unicodeobjects' _unicode_formatter_field_name_split for
+   details */
 static PyObject *
-sys_formatter_lookup(PyObject *self, PyObject *args)
+sys_formatter_field_name_split(PyObject *self, PyObject *args)
 {
         PyObject *field_name;
-        PyObject *arg_args;
-        PyObject *kwargs;
 
-        if (!PyArg_ParseTuple(args, "OOO:_formatter_lookup", &field_name,
-                              &arg_args, &kwargs))
+        if (!PyArg_ParseTuple(args, "O:_formatter_field_name_split",
+                              &field_name))
                 return NULL;
 
         if (!PyUnicode_Check(field_name)) {
-                PyErr_SetString(PyExc_TypeError,
-                                "_formatter_lookup expects unicode object");
+                PyErr_SetString(PyExc_TypeError, "_formatter_field_name_split "
+                                "expects unicode object");
                 return NULL;
         }
 
-        return _unicodeformatter_lookup(field_name, arg_args, kwargs);
+        return _unicodeformatter_field_name_split(field_name);
 }
 
 
@@ -773,7 +773,8 @@
 	{"_current_frames", sys_current_frames, METH_NOARGS,
 	 current_frames_doc},
         {"_formatter_parser", sys_formatter_iterator, METH_VARARGS},
-        {"_formatter_lookup", sys_formatter_lookup, METH_VARARGS},
+        {"_formatter_field_name_split", sys_formatter_field_name_split,
+         METH_VARARGS},
 	{"displayhook",	sys_displayhook, METH_O, displayhook_doc},
 	{"exc_info",	sys_exc_info, METH_NOARGS, exc_info_doc},
 	{"excepthook",	sys_excepthook, METH_VARARGS, excepthook_doc},


More information about the Python-3000-checkins mailing list