This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: Readonly properties should be marked as such in help()
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.8
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: matrixise, rhettinger
Priority: normal Keywords: patch

Created on 2019-03-22 23:17 by rhettinger, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 12517 merged rhettinger, 2019-03-23 17:31
Messages (6)
msg338621 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-03-22 23:17
It is common to create read-only properties with the '@property' decoration but the existing help() output doesn't annotate them as such.

One way to go is to annotate each one separately:

 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from _IPAddressBase:
 |  compressed (read-only property)             <== NEW ANNOTATION
 |      Return the shorthand version of the IP address as a string.
 |  exploded (read-only property)               <== NEW ANNOTATION
 |      Return the longhand version of the IP address as a string.
 |  reverse_pointer (read-only property)        <== NEW ANNOTATION
 |      The name of the reverse DNS pointer for the IP address, e.g.:
 |      >>> ipaddress.ip_address("").reverse_pointer
 |      ''
 |      >>> ipaddress.ip_address("2001:db8::1").reverse_pointer
 |      ''

Another way to go is to break the data descriptor section into two sections --- isolate those that define __set__ or __delete__ from those that don't.

For example, given this code:

    class A:
        'Variety of descriptors and method'

        __slots__ = '_w', '_x'

        def __init__(self, w: int, x: str):
            self.w = w
            self.x = x

        def cm(cls, u):
            'do something with u'
            return cls(u * 4)

        def sm(v):
            'do something with v'
            return v * 3

        def rop(self):
            'computed field'
            return self._w * 2

        def wandr(self):
            'managed attribute'
            return self._w

        def wandr(self, w):
            self._w = w

Produce this help output:

Help on class A in module __main__:

    class A(builtins.object)
     |  A(w: int, x: str)
     |  Variety of descriptors and method
     |  Methods defined here:
     |  __init__(self, w: int, x: str)
     |      initializer
     |  ----------------------------------------------------------------------
     |  Class methods defined here:
     |  cm(u) from builtins.type
     |      do something with u
     |  ----------------------------------------------------------------------
     |  Static methods defined here:
     |  sm(v)
     |      do something with v
     |  ----------------------------------------------------------------------
     |  Read-only descriptors defined here:        <== NEW HEADING
     |  rop
     |      computed field
     |  ----------------------------------------------------------------------
     |  Mutable data descriptors defined here:     <== NEW HEADING AND SECTION    
     |  wandr
     |      managed attribute
msg338627 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-03-22 23:33
For property objects, we have to look at *fset* and *fdel* to find-out whether they are assignable:

    >>> A.rop.fset is None
    >>> A.wandr.fset is None
msg338628 - (view) Author: Stéphane Wirtel (matrixise) * (Python committer) Date: 2019-03-23 00:00


but can we do the same thing with the PyGetSetDef declaration for the C Part?
msg338639 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-03-23 01:14
> but can we do the same thing with the PyGetSetDef declaration 
> for the C Part?

The would likely take an API change.  For now, using only what is already exposed in Python, we can only partition data descriptors in two groups:

* Known to be readonly because __set__ is missing or fset is None
* Possibly writeable, can't really tell until __set__ is called

Example in the latter category,  

    >>> t = time.localtime()
    >>> hasattr(type(t).tm_sec, '__set__')
    >>> t.tm_sec = 31
    Traceback (most recent call last):
      File "<pyshell#23>", line 1, in <module>
         t.tm_sec = 31
    AttributeError: readonly attribute
msg338773 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-03-25 00:07
New changeset 62be33870e2f8517314bf9c7275548e799296f7e by Raymond Hettinger in branch 'master':
bpo-36401: Have help() show readonly properties separately (GH-12517)
msg338781 - (view) Author: Stéphane Wirtel (matrixise) * (Python committer) Date: 2019-03-25 05:18
Hi Raymond,

About the C API, I wanted to know that because I started to use neovim
for the development of CPython mix between C and Python is really great
with this tool. Also, I wanted to have the description of the C parts,
example, when I have PyArg_ParseTupleAndKeywords under the cursor, with
(n)vim I could use the K shortcut and see the description of this
function via the keywordprg of vim.

But we have the result from Sphinx, because the C part is described in
the .rst files. So, maybe I could develop a wrapper for Sphinx and the

So, thank you for your PR.
Date User Action Args
2022-04-11 14:59:12adminsetgithub: 80582
2019-03-25 05:18:30matrixisesetmessages: + msg338781
2019-03-25 03:59:18rhettingersetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2019-03-25 00:07:49rhettingersetmessages: + msg338773
2019-03-23 17:31:32rhettingersetkeywords: + patch
stage: patch review
pull_requests: + pull_request12468
2019-03-23 01:14:57rhettingersetmessages: + msg338639
2019-03-23 00:00:48matrixisesetnosy: + matrixise
messages: + msg338628
2019-03-22 23:33:11rhettingersetmessages: + msg338627
2019-03-22 23:17:42rhettingercreate