Make properties discoverable from Python:
- property() now takes 4 keyword arguments: fget, fset, fdel, doc. Note that the real purpose of the 'f' prefix is to make fdel fit in ('del' is a keyword, so can't used as a keyword argument name). - These map to visible readonly attributes 'fget', 'fset', 'fdel', and '__doc__' in the property object. - fget/fset/fdel weren't discoverable from Python before. - __doc__ is new, and allows to associate a docstring with a property.
This commit is contained in:
parent
30c4849169
commit
66c1a525e0
|
@ -1364,7 +1364,7 @@ def properties():
|
|||
self.__x = value
|
||||
def delx(self):
|
||||
del self.__x
|
||||
x = property(getx, setx, delx)
|
||||
x = property(getx, setx, delx, doc="I'm the x property.")
|
||||
a = C()
|
||||
verify(not hasattr(a, "x"))
|
||||
a.x = 42
|
||||
|
@ -1378,6 +1378,32 @@ def properties():
|
|||
## C.x.__set__(a)
|
||||
## verify(not hasattr(a, "x"))
|
||||
|
||||
raw = C.__dict__['x']
|
||||
verify(isinstance(raw, property))
|
||||
|
||||
attrs = dir(raw)
|
||||
verify("__doc__" in attrs)
|
||||
verify("fget" in attrs)
|
||||
verify("fset" in attrs)
|
||||
verify("fdel" in attrs)
|
||||
|
||||
verify(raw.__doc__ == "I'm the x property.")
|
||||
verify(raw.fget is C.__dict__['getx'])
|
||||
verify(raw.fset is C.__dict__['setx'])
|
||||
verify(raw.fdel is C.__dict__['delx'])
|
||||
|
||||
for attr in "__doc__", "fget", "fset", "fdel":
|
||||
try:
|
||||
setattr(raw, attr, 42)
|
||||
except TypeError, msg:
|
||||
if str(msg).find('readonly') < 0:
|
||||
raise TestFailed("when setting readonly attr %r on a "
|
||||
"property, got unexpected TypeError "
|
||||
"msg %r" % (attr, str(msg)))
|
||||
else:
|
||||
raise TestFailed("expected TypeError from trying to set "
|
||||
"readonly %r attr on a property" % attr)
|
||||
|
||||
def supers():
|
||||
if verbose: print "Testing super..."
|
||||
|
||||
|
@ -1884,7 +1910,7 @@ def rich_comparisons():
|
|||
zz = ZZ(1.0000003)
|
||||
verify(zz == 1+0j)
|
||||
verify(1+0j == zz)
|
||||
|
||||
|
||||
class classic:
|
||||
pass
|
||||
for base in (classic, int, object, list):
|
||||
|
|
|
@ -3,6 +3,12 @@ What's New in Python 2.2a4?
|
|||
|
||||
Core
|
||||
|
||||
- property() now takes 4 keyword arguments: fget, fset, fdel and doc.
|
||||
These map to readonly attributes 'fget', 'fset', 'fdel', and '__doc__'
|
||||
in the constructed property object. fget, fset and fdel weren't
|
||||
discoverable from Python in 2.2a3. __doc__ is new, and allows to
|
||||
associate a docstring with a property.
|
||||
|
||||
- file.writelines() now accepts any iterable object producing strings.
|
||||
|
||||
- PyUnicode_FromEncodedObject() now works very much like
|
||||
|
|
|
@ -873,9 +873,11 @@ PyWrapper_New(PyObject *d, PyObject *self)
|
|||
/*
|
||||
class property(object):
|
||||
|
||||
def __init__(self, get=None, set=None):
|
||||
self.__get = get
|
||||
self.__set = set
|
||||
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
|
||||
self.__get = fget
|
||||
self.__set = fset
|
||||
self.__del = fdel
|
||||
self.__doc__ = doc
|
||||
|
||||
def __get__(self, inst, type=None):
|
||||
if self.__get is None:
|
||||
|
@ -885,26 +887,42 @@ PyWrapper_New(PyObject *d, PyObject *self)
|
|||
return self.__get(inst)
|
||||
|
||||
def __set__(self, inst, value):
|
||||
if self.__set is None:
|
||||
raise AttributeError, "unsettable attribute"
|
||||
return self.__set(inst, value)
|
||||
if value is None:
|
||||
if self.__del is None:
|
||||
raise AttributeError, "can't delete attribute"
|
||||
return self.__del(inst)
|
||||
else:
|
||||
if self.__set is None:
|
||||
raise AttributeError, "can't set attribute"
|
||||
return self.__set(inst, value)
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyObject *get;
|
||||
PyObject *set;
|
||||
PyObject *del;
|
||||
PyObject *prop_get;
|
||||
PyObject *prop_set;
|
||||
PyObject *prop_del;
|
||||
PyObject *prop_doc;
|
||||
} propertyobject;
|
||||
|
||||
static PyMemberDef property_members[] = {
|
||||
{"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY},
|
||||
{"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
|
||||
{"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
|
||||
{"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), READONLY},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
property_dealloc(PyObject *self)
|
||||
{
|
||||
propertyobject *gs = (propertyobject *)self;
|
||||
|
||||
Py_XDECREF(gs->get);
|
||||
Py_XDECREF(gs->set);
|
||||
Py_XDECREF(gs->del);
|
||||
Py_XDECREF(gs->prop_get);
|
||||
Py_XDECREF(gs->prop_set);
|
||||
Py_XDECREF(gs->prop_del);
|
||||
Py_XDECREF(gs->prop_doc);
|
||||
self->ob_type->tp_free(self);
|
||||
}
|
||||
|
||||
|
@ -913,7 +931,7 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
|
|||
{
|
||||
propertyobject *gs = (propertyobject *)self;
|
||||
|
||||
if (gs->get == NULL) {
|
||||
if (gs->prop_get == NULL) {
|
||||
PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -921,7 +939,7 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
|
|||
Py_INCREF(self);
|
||||
return self;
|
||||
}
|
||||
return PyObject_CallFunction(gs->get, "(O)", obj);
|
||||
return PyObject_CallFunction(gs->prop_get, "(O)", obj);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -931,9 +949,9 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
|
|||
PyObject *func, *res;
|
||||
|
||||
if (value == NULL)
|
||||
func = gs->del;
|
||||
func = gs->prop_del;
|
||||
else
|
||||
func = gs->set;
|
||||
func = gs->prop_set;
|
||||
if (func == NULL) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
value == NULL ?
|
||||
|
@ -954,32 +972,45 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
|
|||
static int
|
||||
property_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *get = NULL, *set = NULL, *del = NULL;
|
||||
PyObject *get = NULL, *set = NULL, *del = NULL, *doc = NULL;
|
||||
static char *kwlist[] = {"fget", "fset", "fdel", "doc", 0};
|
||||
propertyobject *gs = (propertyobject *)self;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|OOO:property", &get, &set, &del))
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO:property",
|
||||
kwlist, &get, &set, &del, &doc))
|
||||
return -1;
|
||||
|
||||
if (get == Py_None)
|
||||
get = NULL;
|
||||
if (set == Py_None)
|
||||
set = NULL;
|
||||
if (del == Py_None)
|
||||
del = NULL;
|
||||
|
||||
Py_XINCREF(get);
|
||||
Py_XINCREF(set);
|
||||
Py_XINCREF(del);
|
||||
gs->get = get;
|
||||
gs->set = set;
|
||||
gs->del = del;
|
||||
Py_XINCREF(doc);
|
||||
|
||||
gs->prop_get = get;
|
||||
gs->prop_set = set;
|
||||
gs->prop_del = del;
|
||||
gs->prop_doc = doc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char property_doc[] =
|
||||
"property([getfunc[, setfunc[, delfunc]]]) -> property attribute\n"
|
||||
"Typical use to define a managed attribute x of C instances:\n"
|
||||
"property(fget=None, fset=None, fdel=None, doc=None) -> property attribute\n"
|
||||
"\n"
|
||||
"fget is a function to be used for getting an attribute value, and likewise\n"
|
||||
"fset is a function for setting, and fdel a function for del'ing, an\n"
|
||||
"attribute. Typical use is to define a managed attribute x:\n"
|
||||
"class C(object):\n"
|
||||
" def getx(self): return self.__x\n"
|
||||
" def setx(self, value): self.__x = value\n"
|
||||
" def delx(self): del self.__x\n"
|
||||
" x = property(getx, setx, delx)";
|
||||
" x = property(getx, setx, delx, \"I'm the 'x' property.\")";
|
||||
|
||||
PyTypeObject PyProperty_Type = {
|
||||
PyObject_HEAD_INIT(&PyType_Type)
|
||||
|
@ -1012,7 +1043,7 @@ PyTypeObject PyProperty_Type = {
|
|||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
property_members, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
|
|
Loading…
Reference in New Issue