Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
7ade6485
Commit
7ade6485
authored
Aug 26, 2007
by
Eric Smith
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
PEP 3101: Completed string.Formatter class. Reimplemented field_name to object transformation.
parent
2bf4d5ba
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
497 additions
and
197 deletions
+497
-197
Include/unicodeobject.h
Include/unicodeobject.h
+1
-2
Lib/string.py
Lib/string.py
+35
-7
Lib/test/test_string.py
Lib/test/test_string.py
+21
-2
Lib/test/test_unicode.py
Lib/test/test_unicode.py
+9
-0
Objects/stringlib/string_format.h
Objects/stringlib/string_format.h
+260
-165
Objects/unicodeobject.c
Objects/unicodeobject.c
+157
-8
Python/sysmodule.c
Python/sysmodule.c
+14
-13
No files found.
Include/unicodeobject.h
View file @
7ade6485
...
...
@@ -1440,8 +1440,7 @@ PyAPI_FUNC(Py_UNICODE*) Py_UNICODE_strchr(
PyObject
*
_unicodeformatter_iterator
(
PyObject
*
str
);
PyObject
*
_unicodeformatter_lookup
(
PyObject
*
field_name
,
PyObject
*
args
,
PyObject
*
kwargs
);
_unicodeformatter_field_name_split
(
PyObject
*
field_name
);
#ifdef __cplusplus
}
...
...
Lib/string.py
View file @
7ade6485
...
...
@@ -200,31 +200,59 @@ class Template(metaclass=_TemplateMetaclass):
# 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)
Lib/test/test_string.py
View file @
7ade6485
...
...
@@ -19,8 +19,27 @@ class ModuleTest(unittest.TestCase):
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
):
...
...
Lib/test/test_unicode.py
View file @
7ade6485
...
...
@@ -449,6 +449,10 @@ class UnicodeTest(
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 @@ class UnicodeTest(
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 @@ class UnicodeTest(
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
...
...
Objects/stringlib/string_format.h
View file @
7ade6485
...
...
@@ -72,23 +72,6 @@ SetError(const char *s)
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 @@ output_data(OutputString *output, const STRINGLIB_CHAR *s, Py_ssize_t count)
/*********** 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
;
}
}
Py_ssize_t
accumulator
=
0
;
Py_ssize_t
digitval
;
Py_ssize_t
oldaccumulator
;
STRINGLIB_CHAR
*
p
;
/*
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.
/* 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 @@ get_integer(STRINGLIB_CHAR **ptr, STRINGLIB_CHAR *end,
}
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
)
get
attr
(
PyObject
*
obj
,
SubString
*
name
)
{
STRINGLIB_CHAR
*
start
;
for
(
start
=
input
->
ptr
;
input
->
ptr
<
input
->
end
&&
!
end_identifier
(
*
input
->
ptr
);
input
->
ptr
++
)
;
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
;
}
return
STRINGLIB_NEW
(
start
,
input
->
ptr
-
start
);
/* 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
);
}
/*
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
;
}
/* 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
);
if
(
kwargs
&&
(
result
=
PyDict_GetItem
(
kwargs
,
key
))
!=
NULL
)
{
Py_INCREF
(
result
);
return
result
;
/* 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 @@ key_lookup(PyObject *kwargs, PyObject *key)
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 */
FieldNameIterator
rest
;
myobj
=
args
;
Py_INCREF
(
myobj
);
if
(
!
field_name_split
(
input
->
ptr
,
input
->
end
-
input
->
ptr
,
&
first
,
&
index
,
&
rest
))
{
goto
error
;
}
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
(
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
;
}
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
;
}
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
;
}
}
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"
);
/* 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
;
}
Py_XDECREF
(
myobj
);
/* end of iterator, this is the non-error case */
if
(
ok
==
1
)
return
obj
;
error:
Py_XDECREF
(
obj
);
return
NULL
;
}
...
...
Objects/unicodeobject.c
View file @
7ade6485
...
...
@@ -9161,9 +9161,8 @@ typedef struct {
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 @@ _unicodeformatter_iterator(PyObject *str)
{
formatteriterobject
*
it
;
it
=
PyObject_
GC_
New
(
formatteriterobject
,
&
PyFormatterIter_Type
);
it
=
PyObject_New
(
formatteriterobject
,
&
PyFormatterIter_Type
);
if
(
it
==
NULL
)
return
NULL
;
...
...
@@ -9326,17 +9325,167 @@ _unicodeformatter_iterator(PyObject *str)
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 **************************/
...
...
Python/sysmodule.c
View file @
7ade6485
...
...
@@ -683,28 +683,28 @@ sys_formatter_iterator(PyObject *self, PyObject *args)
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
,
"O
OO:_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 @@ static PyMethodDef sys_methods[] = {
{
"_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
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment