Merged revisions 59488-59511 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r59489 | christian.heimes | 2007-12-14 03:33:57 +0100 (Fri, 14 Dec 2007) | 1 line

  Silence a warning about an unsed variable in debug builds
........
  r59490 | christian.heimes | 2007-12-14 03:35:23 +0100 (Fri, 14 Dec 2007) | 2 lines

  Fixed bug #1620: New @spam.getter property syntax modifies the property in place.
  I added also the feature that a @prop.getter decorator does not overwrite the doc string of the property if it was given as an argument to property().
........
  r59491 | raymond.hettinger | 2007-12-14 03:49:47 +0100 (Fri, 14 Dec 2007) | 1 line

  Cleaner method naming convention
........
  r59492 | christian.heimes | 2007-12-14 04:02:34 +0100 (Fri, 14 Dec 2007) | 1 line

  Fixed a warning in _codecs_iso2022.c and some non C89 conform // comments.
........
  r59493 | christian.heimes | 2007-12-14 05:38:13 +0100 (Fri, 14 Dec 2007) | 1 line

  Fixed warning in ssl module
........
  r59500 | raymond.hettinger | 2007-12-14 19:08:20 +0100 (Fri, 14 Dec 2007) | 1 line

  Add line spacing for readability
........
  r59501 | raymond.hettinger | 2007-12-14 19:12:21 +0100 (Fri, 14 Dec 2007) | 3 lines

  Update method names for named tuples.
........
  r59503 | georg.brandl | 2007-12-14 20:03:36 +0100 (Fri, 14 Dec 2007) | 3 lines

  Add a section about nested listcomps to the tutorial.
  Thanks to Ian Bruntlett and Robert Lehmann.
........
  r59504 | raymond.hettinger | 2007-12-14 20:19:59 +0100 (Fri, 14 Dec 2007) | 1 line

  Faster and simpler _replace() method
........
  r59505 | raymond.hettinger | 2007-12-14 22:51:50 +0100 (Fri, 14 Dec 2007) | 1 line

  Add usage note
........
  r59507 | andrew.kuchling | 2007-12-14 23:41:18 +0100 (Fri, 14 Dec 2007) | 1 line

  Remove warning about URL
........
  r59510 | andrew.kuchling | 2007-12-14 23:52:36 +0100 (Fri, 14 Dec 2007) | 1 line

  Bump the version number, and make a few small edits
........
  r59511 | christian.heimes | 2007-12-15 00:42:36 +0100 (Sat, 15 Dec 2007) | 2 lines

  Fixed bug #1628
  The detection now works on Unix with Makefile, Makefile with VPATH and on Windows.
........
This commit is contained in:
Christian Heimes 2007-12-15 01:27:15 +00:00
parent 54cc54c1fe
commit 0449f63f53
16 changed files with 299 additions and 103 deletions

View File

@ -28,6 +28,7 @@ docs@python.org), and we'll be glad to correct the problem.
* Aaron Brancotti
* Georg Brandl
* Keith Briggs
* Ian Bruntlett
* Lee Busby
* Lorenzo M. Catucci
* Carl Cerecke

View File

@ -3,12 +3,10 @@
********************************
:Author: \A. M. Kuchling
:Release: 0.30
:Release: 0.31
(This is a first draft. Please send comments/error reports/suggestions to
amk@amk.ca. This URL is probably not going to be the final location of the
document, so be careful about linking to it -- you may want to add a
disclaimer.)
amk@amk.ca.)
In this document, we'll take a tour of Python's features suitable for
implementing programs in a functional style. After an introduction to the
@ -49,17 +47,19 @@ Programming languages support decomposing problems in several different ways:
functional languages include the ML family (Standard ML, OCaml, and other
variants) and Haskell.
The designers of some computer languages have chosen one approach to programming
that's emphasized. This often makes it difficult to write programs that use a
different approach. Other languages are multi-paradigm languages that support
several different approaches. Lisp, C++, and Python are multi-paradigm; you can
write programs or libraries that are largely procedural, object-oriented, or
functional in all of these languages. In a large program, different sections
might be written using different approaches; the GUI might be object-oriented
while the processing logic is procedural or functional, for example.
The designers of some computer languages choose to emphasize one
particular approach to programming. This often makes it difficult to
write programs that use a different approach. Other languages are
multi-paradigm languages that support several different approaches.
Lisp, C++, and Python are multi-paradigm; you can write programs or
libraries that are largely procedural, object-oriented, or functional
in all of these languages. In a large program, different sections
might be written using different approaches; the GUI might be
object-oriented while the processing logic is procedural or
functional, for example.
In a functional program, input flows through a set of functions. Each function
operates on its input and produces some output. Functional style frowns upon
operates on its input and produces some output. Functional style discourages
functions with side effects that modify internal state or make other changes
that aren't visible in the function's return value. Functions that have no side
effects at all are called **purely functional**. Avoiding side effects means
@ -616,7 +616,7 @@ Built-in functions
Let's look in more detail at built-in functions often used with iterators.
Two Python's built-in functions, :func:`map` and :func:`filter`, are somewhat
Two of Python's built-in functions, :func:`map` and :func:`filter`, are somewhat
obsolete; they duplicate the features of list comprehensions but return actual
lists instead of iterators.
@ -842,8 +842,8 @@ Fredrik Lundh once suggested the following set of rules for refactoring uses of
4) Convert the lambda to a def statement, using that name.
5) Remove the comment.
I really like these rules, but you're free to disagree that this lambda-free
style is better.
I really like these rules, but you're free to disagree
about whether this lambda-free style is better.
The itertools module

View File

@ -403,8 +403,8 @@ they add the ability to access fields by name instead of position index.
can be specified as a list of strings (such as ['x', 'y']).
Any valid Python identifier may be used for a fieldname except for names
starting and ending with double underscores. Valid identifiers consist of
letters, digits, and underscores but do not start with a digit and cannot be
starting with an underscore. Valid identifiers consist of letters, digits,
and underscores but do not start with a digit or underscore and cannot be
a :mod:`keyword` such as *class*, *for*, *return*, *global*, *pass*, *print*,
or *raise*.
@ -418,18 +418,25 @@ Example::
>>> Point = namedtuple('Point', 'x y', verbose=True)
class Point(tuple):
'Point(x, y)'
__slots__ = ()
__fields__ = ('x', 'y')
_fields = ('x', 'y')
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
def __repr__(self):
return 'Point(x=%r, y=%r)' % self
def __asdict__(self):
'Return a new dict mapping field names to their values'
def _asdict(self):
'Return a new dict which maps field names to their values'
return dict(zip(('x', 'y'), self))
def __replace__(self, **kwds):
def _replace(self, **kwds):
'Return a new Point object replacing specified fields with new values'
return Point(**dict(zip(('x', 'y'), self), **kwds))
return Point(*map(kwds.get, ('x', 'y'), self))
x = property(itemgetter(0))
y = property(itemgetter(1))
@ -477,43 +484,51 @@ When casting a dictionary to a named tuple, use the double-star-operator::
In addition to the methods inherited from tuples, named tuples support
two additonal methods and a read-only attribute.
.. method:: somenamedtuple.__asdict__()
.. method:: somenamedtuple._asdict()
Return a new dict which maps field names to their corresponding values:
::
>>> p.__asdict__()
>>> p._asdict()
{'x': 11, 'y': 22}
.. method:: somenamedtuple.__replace__(kwargs)
.. method:: somenamedtuple._replace(kwargs)
Return a new instance of the named tuple replacing specified fields with new values:
::
>>> p = Point(x=11, y=22)
>>> p.__replace__(x=33)
>>> p._replace(x=33)
Point(x=33, y=22)
>>> for partnum, record in inventory.items():
... inventory[partnum] = record.__replace__(price=newprices[partnum], updated=time.now())
... inventory[partnum] = record._replace(price=newprices[partnum], updated=time.now())
.. attribute:: somenamedtuple.__fields__
.. attribute:: somenamedtuple._fields
Return a tuple of strings listing the field names. This is useful for introspection
and for creating new named tuple types from existing named tuples.
::
>>> p.__fields__ # view the field names
>>> p._fields # view the field names
('x', 'y')
>>> Color = namedtuple('Color', 'red green blue')
>>> Pixel = namedtuple('Pixel', Point.__fields__ + Color.__fields__)
>>> Pixel = namedtuple('Pixel', Point._fields + Color._fields)
>>> Pixel(11, 22, 128, 255, 0)
Pixel(x=11, y=22, red=128, green=255, blue=0)'
To retrieve a field whose name is stored in a string, use the :func:`getattr`
function:
::
>>> getattr(p, 'x')
11
Since a named tuple is a regular Python class, it is easy to add or change
functionality. For example, the display format can be changed by overriding
the :meth:`__repr__` method:
@ -522,17 +537,17 @@ the :meth:`__repr__` method:
>>> Point = namedtuple('Point', 'x y')
>>> Point.__repr__ = lambda self: 'Point(%.3f, %.3f)' % self
>>> Point(x=10, y=20)
Point(10.000, 20.000)
>>> Point(x=11, y=22)
Point(11.000, 22.000)
Default values can be implemented by starting with a prototype instance
and customizing it with :meth:`__replace__`:
and customizing it with :meth:`_replace`:
::
>>> Account = namedtuple('Account', 'owner balance transaction_count')
>>> model_account = Account('<owner name>', 0.0, 0)
>>> johns_account = model_account.__replace__(owner='John')
>>> johns_account = model_account._replace(owner='John')
.. rubric:: Footnotes

View File

@ -277,6 +277,48 @@ List comprehensions can be applied to complex expressions and nested functions::
['3.1', '3.14', '3.142', '3.1416', '3.14159']
Nested List Comprehensions
--------------------------
If you've got the stomach for it, list comprehensions can be nested. They are a
powerful tool but -- like all powerful tools -- they need to be used carefully,
if at all.
Consider the following example of a 3x3 matrix held as a list containing three
lists, one list per row::
>>> mat = [
... [1, 2, 3],
... [4, 5, 6],
... [7, 8, 9],
... ]
Now, if you wanted to swap rows and columns, you could use a list
comprehension::
>>> print [[row[i] for row in mat] for i in [0, 1, 2]]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Special care has to be taken for the *nested* list comprehension:
To avoid apprehension when nesting list comprehensions, read from right to
left.
A more verbose version of this snippet shows the flow explicitly::
for i in [0, 1, 2]:
for row in mat:
print row[i],
print
In real world, you should prefer builtin functions to complex flow statements.
The :func:`zip` function would do a great job for this use case::
>>> zip(*mat)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
See :ref:`tut-unpacking-arguments` for details on the asterisk in this line.
.. _tut-del:
The :keyword:`del` statement

View File

@ -528,7 +528,7 @@ complete list of changes, or look through the CVS logs for all the details.
... 'id name type size')
# Names are separated by spaces or commas.
# 'id, name, type, size' would also work.
>>> var_type.__fields__
>>> var_type._fields
('id', 'name', 'type', 'size')
>>> var = var_type(1, 'frequency', 'int', 4)
@ -536,9 +536,9 @@ complete list of changes, or look through the CVS logs for all the details.
1 1
>>> print var[2], var.type # Equivalent
int int
>>> var.__asdict__()
>>> var._asdict()
{'size': 4, 'type': 'int', 'id': 1, 'name': 'frequency'}
>>> v2 = var.__replace__('name', 'amplitude')
>>> v2 = var._replace('name', 'amplitude')
>>> v2
variable(id=1, name='amplitude', type='int', size=4)

View File

@ -26,12 +26,12 @@ def namedtuple(typename, field_names, verbose=False):
(11, 22)
>>> p.x + p.y # fields also accessable by name
33
>>> d = p.__asdict__() # convert to a dictionary
>>> d = p._asdict() # convert to a dictionary
>>> d['x']
11
>>> Point(**d) # convert from a dictionary
Point(x=11, y=22)
>>> p.__replace__(x=100) # __replace__() is like str.replace() but targets named fields
>>> p._replace(x=100) # _replace() is like str.replace() but targets named fields
Point(x=100, y=22)
"""
@ -49,8 +49,8 @@ def namedtuple(typename, field_names, verbose=False):
raise ValueError('Type names and field names cannot start with a number: %r' % name)
seen_names = set()
for name in field_names:
if name.startswith('__') and name.endswith('__') and len(name) > 3:
raise ValueError('Field names cannot start and end with double underscores: %r' % name)
if name.startswith('_'):
raise ValueError('Field names cannot start with an underscore: %r' % name)
if name in seen_names:
raise ValueError('Encountered duplicate field name: %r' % name)
seen_names.add(name)
@ -59,19 +59,19 @@ def namedtuple(typename, field_names, verbose=False):
argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
reprtxt = ', '.join('%s=%%r' % name for name in field_names)
template = '''class %(typename)s(tuple):
'%(typename)s(%(argtxt)s)'
__slots__ = ()
__fields__ = property(lambda self: %(field_names)r)
'%(typename)s(%(argtxt)s)' \n
__slots__ = () \n
_fields = property(lambda self: %(field_names)r) \n
def __new__(cls, %(argtxt)s):
return tuple.__new__(cls, (%(argtxt)s))
return tuple.__new__(cls, (%(argtxt)s)) \n
def __repr__(self):
return '%(typename)s(%(reprtxt)s)' %% self
def __asdict__(self, dict=dict, zip=zip):
'Return a new dict mapping field names to their values'
return dict(zip(%(field_names)r, self))
def __replace__(self, **kwds):
return '%(typename)s(%(reprtxt)s)' %% self \n
def _asdict(self, dict=dict, zip=zip):
'Return a new dict which maps field names to their values'
return dict(zip(%(field_names)r, self)) \n
def _replace(self, **kwds):
'Return a new %(typename)s object replacing specified fields with new values'
return %(typename)s(**dict(zip(%(field_names)r, self), **kwds)) \n''' % locals()
return %(typename)s(*map(kwds.get, %(field_names)r, self)) \n\n''' % locals()
for i, name in enumerate(field_names):
template += ' %s = property(itemgetter(%d))\n' % (name, i)
if verbose:

View File

@ -31,8 +31,10 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower():
# python_build: (Boolean) if true, we're either building Python or
# building an extension with an un-installed Python, so we use
# different (hard-wired) directories.
python_build = os.path.isfile(os.path.join(project_base, "Modules",
"Setup.local"))
# Setup.local is available for Makefile builds including VPATH builds,
# Setup.dist is available on Windows
python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn))
for fn in ("Setup.dist", "Setup.local"))
def get_python_version():
"""Return a string containing the major and minor Python version,

View File

@ -15,7 +15,7 @@ class SysconfigTestCase(unittest.TestCase):
def test_get_python_lib(self):
lib_dir = sysconfig.get_python_lib()
# XXX doesn't work on Inux when Python was never installed before
# XXX doesn't work on Linux when Python was never installed before
#self.assert_(os.path.isdir(lib_dir), lib_dir)
# test for pythonxx.lib?

View File

@ -28,11 +28,11 @@ class TestNamedTuple(unittest.TestCase):
self.assertRaises(ValueError, namedtuple, 'abc', 'efg g%hi') # field with non-alpha char
self.assertRaises(ValueError, namedtuple, 'abc', 'abc class') # field has keyword
self.assertRaises(ValueError, namedtuple, 'abc', '8efg 9ghi') # field starts with digit
self.assertRaises(ValueError, namedtuple, 'abc', '__efg__ ghi') # field with double underscores
self.assertRaises(ValueError, namedtuple, 'abc', '_efg ghi') # field with leading underscore
self.assertRaises(ValueError, namedtuple, 'abc', 'efg efg ghi') # duplicate field
namedtuple('Point0', 'x1 y2') # Verify that numbers are allowed in names
namedtuple('_', '_ __ ___') # Verify that underscores are allowed
namedtuple('_', 'a b c') # Test leading underscores in a typename
def test_instance(self):
Point = namedtuple('Point', 'x y')
@ -49,17 +49,17 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(repr(p), 'Point(x=11, y=22)')
self.assert_('__dict__' not in dir(p)) # verify instance has no dict
self.assert_('__weakref__' not in dir(p))
self.assertEqual(p.__fields__, ('x', 'y')) # test __fields__ attribute
self.assertEqual(p.__replace__(x=1), (1, 22)) # test __replace__ method
self.assertEqual(p.__asdict__(), dict(x=11, y=22)) # test __dict__ method
self.assertEqual(p._fields, ('x', 'y')) # test _fields attribute
self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method
self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method
# Verify that __fields__ is read-only
# Verify that _fields is read-only
try:
p.__fields__ = ('F1' ,'F2')
p._fields = ('F1' ,'F2')
except AttributeError:
pass
else:
self.fail('The __fields__ attribute needs to be read-only')
self.fail('The _fields attribute needs to be read-only')
# verify that field string can have commas
Point = namedtuple('Point', 'x, y')

98
Lib/test/test_property.py Normal file
View File

@ -0,0 +1,98 @@
# Test case for property
# more tests are in test_descr
import unittest
from test.test_support import run_unittest
class PropertyBase(Exception):
pass
class PropertyGet(PropertyBase):
pass
class PropertySet(PropertyBase):
pass
class PropertyDel(PropertyBase):
pass
class BaseClass(object):
def __init__(self):
self._spam = 5
@property
def spam(self):
"""BaseClass.getter"""
return self._spam
@spam.setter
def spam(self, value):
self._spam = value
@spam.deleter
def spam(self):
del self._spam
class SubClass(BaseClass):
@BaseClass.spam.getter
def spam(self):
"""SubClass.getter"""
raise PropertyGet(self._spam)
@spam.setter
def spam(self, value):
raise PropertySet(self._spam)
@spam.deleter
def spam(self):
raise PropertyDel(self._spam)
class PropertyDocBase(object):
_spam = 1
def _get_spam(self):
return self._spam
spam = property(_get_spam, doc="spam spam spam")
class PropertyDocSub(PropertyDocBase):
@PropertyDocBase.spam.getter
def spam(self):
"""The decorator does not use this doc string"""
return self._spam
class PropertyTests(unittest.TestCase):
def test_property_decorator_baseclass(self):
# see #1620
base = BaseClass()
self.assertEqual(base.spam, 5)
self.assertEqual(base._spam, 5)
base.spam = 10
self.assertEqual(base.spam, 10)
self.assertEqual(base._spam, 10)
delattr(base, "spam")
self.assert_(not hasattr(base, "spam"))
self.assert_(not hasattr(base, "_spam"))
base.spam = 20
self.assertEqual(base.spam, 20)
self.assertEqual(base._spam, 20)
self.assertEqual(base.__class__.spam.__doc__, "BaseClass.getter")
def test_property_decorator_subclass(self):
# see #1620
sub = SubClass()
self.assertRaises(PropertyGet, getattr, sub, "spam")
self.assertRaises(PropertySet, setattr, sub, "spam", None)
self.assertRaises(PropertyDel, delattr, sub, "spam")
self.assertEqual(sub.__class__.spam.__doc__, "SubClass.getter")
def test_property_decorator_doc(self):
base = PropertyDocBase()
sub = PropertyDocSub()
self.assertEqual(base.__class__.spam.__doc__, "spam spam spam")
self.assertEqual(sub.__class__.spam.__doc__, "spam spam spam")
def test_main():
run_unittest(PropertyTests)
if __name__ == '__main__':
test_main()

View File

@ -256,7 +256,7 @@ ffi_closure_SYSV_inner (closure, respp, args)
void **respp;
void *args;
{
// our various things...
/* our various things... */
ffi_cif *cif;
void **arg_area;

View File

@ -656,7 +656,7 @@ _get_peer_alt_names (X509 *certificate) {
char buf[2048];
char *vptr;
int len;
unsigned char *p;
const unsigned char *p;
if (certificate == NULL)
return peer_alt_names;

View File

@ -232,7 +232,7 @@ typedef struct {
#define DBSequenceObject_Check(v) ((v)->ob_type == bsddb_api->dbsequence_type)
#endif
#endif // COMPILING_BSDDB_C
#endif /* COMPILING_BSDDB_C */
#endif // _BSDDB_H_
#endif /* _BSDDB_H_ */

View File

@ -203,7 +203,7 @@ ENCODER(iso2022)
} else
encoded = dsg->encoder(&c, &length);
#else
encoded = dsg->encoder(*inbuf, &length);
encoded = dsg->encoder(&c, &length);
#endif
if (encoded != MAP_UNMAPPABLE) {
insize = length;

View File

@ -1065,8 +1065,12 @@ typedef struct {
PyObject *prop_set;
PyObject *prop_del;
PyObject *prop_doc;
int getter_doc;
} propertyobject;
static PyObject * property_copy(PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *);
static PyMemberDef property_members[] = {
{"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY},
{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
@ -1075,53 +1079,37 @@ static PyMemberDef property_members[] = {
{0}
};
PyDoc_STRVAR(getter_doc,
"Descriptor to change the getter on a property.");
PyObject *
property_getter(PyObject *self, PyObject *getter)
{
Py_XDECREF(((propertyobject *)self)->prop_get);
if (getter == Py_None)
getter = NULL;
Py_XINCREF(getter);
((propertyobject *)self)->prop_get = getter;
Py_INCREF(self);
return self;
return property_copy(self, getter, NULL, NULL, NULL);
}
PyDoc_STRVAR(setter_doc,
"Descriptor to change the setter on a property.\n");
"Descriptor to change the setter on a property.");
PyObject *
property_setter(PyObject *self, PyObject *setter)
{
Py_XDECREF(((propertyobject *)self)->prop_set);
if (setter == Py_None)
setter = NULL;
Py_XINCREF(setter);
((propertyobject *)self)->prop_set = setter;
Py_INCREF(self);
return self;
return property_copy(self, NULL, setter, NULL, NULL);
}
PyDoc_STRVAR(deleter_doc,
"Descriptor to change the deleter on a property.");
PyObject *
property_deleter(PyObject *self, PyObject *deleter)
{
Py_XDECREF(((propertyobject *)self)->prop_del);
if (deleter == Py_None)
deleter = NULL;
Py_XINCREF(deleter);
((propertyobject *)self)->prop_del = deleter;
Py_INCREF(self);
return self;
return property_copy(self, NULL, NULL, deleter, NULL);
}
static PyMethodDef property_methods[] = {
{"getter", property_getter, METH_O, getter_doc},
{"setter", property_setter, METH_O, setter_doc},
@ -1186,15 +1174,62 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
return 0;
}
static PyObject *
property_copy(PyObject *old, PyObject *get, PyObject *set, PyObject *del,
PyObject *doc)
{
propertyobject *pold = (propertyobject *)old;
propertyobject *pnew = NULL;
PyObject *new, *type;
type = PyObject_Type(old);
if (type == NULL)
return NULL;
if (get == NULL || get == Py_None) {
Py_XDECREF(get);
get = pold->prop_get ? pold->prop_get : Py_None;
}
if (set == NULL || set == Py_None) {
Py_XDECREF(set);
set = pold->prop_set ? pold->prop_set : Py_None;
}
if (del == NULL || del == Py_None) {
Py_XDECREF(del);
del = pold->prop_del ? pold->prop_del : Py_None;
}
if (doc == NULL || doc == Py_None) {
Py_XDECREF(doc);
doc = pold->prop_doc ? pold->prop_doc : Py_None;
}
new = PyObject_CallFunction(type, "OOOO", get, set, del, doc);
if (new == NULL)
return NULL;
pnew = (propertyobject *)new;
if (pold->getter_doc && get != Py_None) {
PyObject *get_doc = PyObject_GetAttrString(get, "__doc__");
if (get_doc != NULL) {
Py_XDECREF(pnew->prop_doc);
pnew->prop_doc = get_doc; /* get_doc already INCREF'd by GetAttr */
pnew->getter_doc = 1;
} else {
PyErr_Clear();
}
}
return new;
}
static int
property_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL;
static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0};
propertyobject *gs = (propertyobject *)self;
propertyobject *prop = (propertyobject *)self;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property",
kwlist, &get, &set, &del, &doc))
kwlist, &get, &set, &del, &doc))
return -1;
if (get == Py_None)
@ -1209,22 +1244,24 @@ property_init(PyObject *self, PyObject *args, PyObject *kwds)
Py_XINCREF(del);
Py_XINCREF(doc);
prop->prop_get = get;
prop->prop_set = set;
prop->prop_del = del;
prop->prop_doc = doc;
prop->getter_doc = 0;
/* if no docstring given and the getter has one, use that one */
if ((doc == NULL || doc == Py_None) && get != NULL) {
PyObject *get_doc = PyObject_GetAttrString(get, "__doc__");
if (get_doc != NULL) {
Py_XDECREF(doc);
doc = get_doc; /* get_doc already INCREF'd by GetAttr */
Py_XDECREF(prop->prop_doc);
prop->prop_doc = get_doc; /* get_doc already INCREF'd by GetAttr */
prop->getter_doc = 1;
} else {
PyErr_Clear();
}
}
gs->prop_get = get;
gs->prop_set = set;
gs->prop_del = del;
gs->prop_doc = doc;
return 0;
}

View File

@ -668,8 +668,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \
lltrace && prtrace(TOP(), "stackadj")); \
assert(STACK_LEVEL() <= co->co_stacksize); }
#define EXT_POP(STACK_POINTER) (lltrace && prtrace((STACK_POINTER)[-1], \
"ext_pop"), *--(STACK_POINTER))
#define EXT_POP(STACK_POINTER) ((void)(lltrace && \
prtrace((STACK_POINTER)[-1], "ext_pop")), \
*--(STACK_POINTER))
#else
#define PUSH(v) BASIC_PUSH(v)
#define POP() BASIC_POP()