Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readonly properties should be marked as such in help() #80582

Closed
rhettinger opened this issue Mar 22, 2019 · 6 comments
Closed

Readonly properties should be marked as such in help() #80582

rhettinger opened this issue Mar 22, 2019 · 6 comments
Labels
3.8 only security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@rhettinger
Copy link
Contributor

BPO 36401
Nosy @rhettinger, @matrixise
PRs
  • bpo-36401: Have help() show readonly properties separately #12517
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2019-03-25.03:59:18.309>
    created_at = <Date 2019-03-22.23:17:42.474>
    labels = ['3.8', 'type-feature', 'library']
    title = 'Readonly properties should be marked as such in help()'
    updated_at = <Date 2019-03-25.05:18:30.828>
    user = 'https://github.com/rhettinger'

    bugs.python.org fields:

    activity = <Date 2019-03-25.05:18:30.828>
    actor = 'matrixise'
    assignee = 'none'
    closed = True
    closed_date = <Date 2019-03-25.03:59:18.309>
    closer = 'rhettinger'
    components = ['Library (Lib)']
    creation = <Date 2019-03-22.23:17:42.474>
    creator = 'rhettinger'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 36401
    keywords = ['patch']
    message_count = 6.0
    messages = ['338621', '338627', '338628', '338639', '338773', '338781']
    nosy_count = 2.0
    nosy_names = ['rhettinger', 'matrixise']
    pr_nums = ['12517']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue36401'
    versions = ['Python 3.8']

    @rhettinger
    Copy link
    Contributor Author

    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("127.0.0.1").reverse_pointer
     |      '1.0.0.127.in-addr.arpa'
     |      >>> ipaddress.ip_address("2001:db8::1").reverse_pointer
     |      '1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa'

    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):
                'initializer'
                self.w = w
                self.x = x
    
            @classmethod
            def cm(cls, u):
                'do something with u'
                return cls(u * 4)
    
            @staticmethod
            def sm(v):
                'do something with v'
                return v * 3
    
            @property
            def rop(self):
                'computed field'
                return self._w * 2
    
            @property
            def wandr(self):
                'managed attribute'
                return self._w
    
            @wandr.setter
            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
    

    @rhettinger rhettinger added 3.8 only security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement labels Mar 22, 2019
    @rhettinger
    Copy link
    Contributor Author

    For property objects, we have to look at *fset* and *fdel* to find-out whether they are assignable:

        >>> A.rop.fset is None
        True
        >>> A.wandr.fset is None
        False

    @matrixise
    Copy link
    Member

    @Raymond

    +1

    but can we do the same thing with the PyGetSetDef declaration for the C Part?

    @rhettinger
    Copy link
    Contributor Author

    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__')
        True
        >>> t.tm_sec = 31
        Traceback (most recent call last):
          File "<pyshell#23>", line 1, in <module>
             t.tm_sec = 31
        AttributeError: readonly attribute

    @rhettinger
    Copy link
    Contributor Author

    New changeset 62be338 by Raymond Hettinger in branch 'master':
    bpo-36401: Have help() show readonly properties separately (GH-12517)
    62be338

    @matrixise
    Copy link
    Member

    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
    manpages.

    So, thank you for your PR.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.8 only security fixes stdlib Python modules in the Lib dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    2 participants