[Python-bugs-list] [ python-Bugs-576990 ] inheriting from property and docstrings

SourceForge.net noreply@sourceforge.net
Fri, 16 May 2003 19:39:17 -0700


Bugs item #576990, was opened at 2002-07-03 07:42
Message generated for change (Comment added) made by bcannon
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=576990&group_id=5470

Category: Python Interpreter Core
Group: Python 2.2.1
Status: Open
Resolution: Accepted
Priority: 5
Submitted By: Roeland Rengelink (rengelink)
Assigned to: Guido van Rossum (gvanrossum)
Summary: inheriting from property and docstrings

Initial Comment:
If I inherit from property, and try to initialize a derived   
property object, the doc string doesn't get set. This bug   
was introduced in 2.2.1, and is present in 2.3a0:   
   
Compare:   
   
Python 2.2 (#1, Mar 26 2002, 15:46:04)   
[GCC 2.95.3 20010315 (SuSE)] on linux2   
Type "help", "copyright", "credits" or "license" for more   
information.   
>>> class myprop(property):pass   
...   
>>> a = myprop(None, None, None, 'hi')   
>>> print a.__doc__   
hi   
   
and,   
   
Python 2.2.1 (#1, Jun 16 2002, 16:19:48)   
[GCC 2.95.3 20010315 (SuSE)] on linux2   
Type "help", "copyright", "credits" or "license" for more   
information.   
>>> class myprop(property):pass   
...   
>>> a = myprop(None, None, None, 'hi')   
>>> print a.__doc__   
None   
   
There is no problem with the getter/setter functions  
passed to the constructor.  i.e.: myprop(f,g,h,None) works   
identical in 2.2 and 2.2.1   
 
Good luck, 
 
Roeland Rengelink   
   
   

----------------------------------------------------------------------

>Comment By: Brett Cannon (bcannon)
Date: 2003-05-16 19:39

Message:
Logged In: YES 
user_id=357491

I vaguely remember this coming on up on python-dev and it being said that 
this would not get fixed.  Am I remembering correctly?

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2002-09-24 10:01

Message:
Logged In: YES 
user_id=6380

The problem is that class myprop has a __doc__ in its class
__dict__ that is the docstring for myprop, or None if that
class has no docstring. The march through the MRO looking
for an instance attribute descriptor finds this before it
would ever get to the property class, so effectively a
__doc__ property is not inherited.

The simplest workaround I found is this:

class myprop(property):
    __doc__ = property.__dict__['__doc__']

(__doc__ = property.__doc__ does not do the right thing, it
gets the property class's docstring rather than the descriptor.)

This is a mess. I'll have to think about whether it's
possible at all to fix it without breaking something else.
I'll also have to think about whether it's worth it.

----------------------------------------------------------------------

Comment By: Thomas Heller (theller)
Date: 2002-09-24 06:12

Message:
Logged In: YES 
user_id=11105

Ups, forget my patch. Nothing works, sorry.

----------------------------------------------------------------------

Comment By: Thomas Heller (theller)
Date: 2002-09-24 06:06

Message:
Logged In: YES 
user_id=11105

The attached simple fix (descrobj.diff) fixes the problem
for me.

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2002-09-24 05:43

Message:
Logged In: YES 
user_id=6380

OK, I'll have a look. We've have numerous hacks upon hacks
to get __doc__ to behave right. I don't know if it is within
my powers to fix this without breaking other things. But if
it is, I'll try to fix it in 2.2.1 as well as 2.3.

----------------------------------------------------------------------

Comment By: Roeland Rengelink (rengelink)
Date: 2002-09-09 03:49

Message:
Logged In: YES 
user_id=302601

To give a usage example: 
(because rhettinger thought this was a very strange use of
properties, and, for all I know, he may be right)

>>> class typed_property(property):
...     def __init__(self, tp, doc):
...             def getter(inst):
...                     return inst.__dict__[self]
...             def setter(inst, val):
...                     if not isinstance(val, tp):
...                             raise TypeError
...                     inst.__dict__[self] = val
...             property.__init__(self, getter, setter, 
...                               None, doc)
... 
>>> class A(object):
...     a = typed_property(int, 'a prop')
... 
>>> inst = A()
>>> inst.a = 1
>>> print inst.a
1
>>> inst.a = 'a'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 7, in setter
TypeError
>>> A.a.__doc__
'a prop'

The last only works in 2.2, and then only if typed_property
itself has no doc string


----------------------------------------------------------------------

Comment By: Raymond Hettinger (rhettinger)
Date: 2002-09-08 15:11

Message:
Logged In: YES 
user_id=80475

Okay, now I see what you're trying to do.
Still, it the strangest use of property that I've seen to-date.
Re-opening the bug report.

----------------------------------------------------------------------

Comment By: Raymond Hettinger (rhettinger)
Date: 2002-09-08 14:55

Message:
Logged In: YES 
user_id=80475

I think this is based on a misunderstanding of how to use 
property.  Instead of inheriting from it, you use it as a 
function call in a new-style class (derived from object):

This works fine in versions from 2.2 upto 2.3a:

>>> class Myprop(object):
          a = property(None,None,None,'a sample docstring')

>>> Myprop.a.__doc__
'a sample docstring'

Marking as invalid and closing.

----------------------------------------------------------------------

Comment By: Roeland Rengelink (rengelink)
Date: 2002-07-09 04:52

Message:
Logged In: YES 
user_id=302601

I think I found the problem, 
 
Compare: 
 
>>> property.__doc__ 
'property(fget=None,.... # the doc string 
 
and 
 
>>> property.__dict__['__doc__'] 
<member '__doc__' of 'property' objects> 
 
Note that property.__doc__ and property.__dict__['__doc__'] 
are not the same. Python will go out of its way to prevent this 
weird situation in user derived classes., 
 
1. type_new(name, bases, attrs) will copy attrs to 
new_tp.tp_dict, and will also copy attrs['__doc__'] to tp.tp_doc 
 
2. PyType_Ready(tp) will copy tp.tp_doc to 
tp.tp_dict['__doc__'] if tp.tp_dict['__doc__'] is undefined 
 
This guarantees that tp.tp_dict['__doc__'] will exist, usually 
copying tp.tp_doc, and shadowing property.tp_dict['__doc__'] 
if tp is derived from property 
 
The solution seems to be: 
 
1. In type_new(): 
   if possible copy attr['__doc__'] to tp.tp_doc, and delete 
__doc__ from attr (to prevent ending up in tp_dict) 
 
2. in PyType_Ready(): 
   don't copy tp.tp_doc to tp_dict['__doc__'] 
 
These two steps make sure that, tp_dict['__doc__'] no longer 
shadows properties.tp_dict['__doc__']. Unfortunately, this 
means that tp.__doc__ doesn't generally return the docstrings 
for user-defined types. Therefore: 
 
3. in type_get_doc(): 
   return tp.tp_doc also for heap types. 
 
The result of this will be: 
 
1. properties will be subclassable again.  (good) 
2. __doc__ string become read-only attributes (bad??) 
3. test cases in test_descr that assume that 
instance.__dict__['__doc__'] exists, will fail 
4. a weird test_failure in test_module.py 
 
Patches for this modification are attached, except for 
(test_module.py), which I don't understand. 

----------------------------------------------------------------------

Comment By: Roeland Rengelink (rengelink)
Date: 2002-07-08 05:23

Message:
Logged In: YES 
user_id=302601

Some more details:

In fact 2.2.1 is consistently wrong, whereas 2.2 is
inconsistently right ;), compare:

Python 2.2.1 (#20, Jul  8 2002, 13:25:14)
[GCC 3.1] on linux2
Type "help", "copyright", "credits" or "license" for more
information.
>>> class myprop(property):pass
...
>>> class yourprop(property):
...     "A doc string"
...
>>> print myprop(None, None, None, 'Hi there').__doc__
None                 
>>> print yourprop(None, None, None, 'Hi there').__doc__
A doc string

and

Python 2.2 (#4, Jan  7 2002, 11:59:25)
[GCC 2.95.2 19991024 (release)] on linux2
Type "help", "copyright", "credits" or "license" for more
information.
>>> class myprop(property):pass
...
>>> class yourprop(property):
...     "A doc string"
...
>>> print myprop(None, None, None, 'Hi there').__doc__
Hi there
>>> print yourprop(None, None, None, 'Hi there').__doc__
A doc string

So, in 2.2.1 myprop(...).__doc__ will allways return
myprop.__doc__. In 2.2 myprop.__doc__ will return the
instance's
__doc__, iff myprop.__doc__ is None.

For the record: I was expecting 'Hi there' (i.e.
obj->prop_doc), as in:

>>> property(None, None, None, 'Hi there').__doc__
'Hi there'

Hope this helps,

Roeland

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=576990&group_id=5470