Skip to content

Docs: Descript __get__ signature defined differently across the docs #80924

Closed

Description

@jdufresne
mannequin
BPO 36743
Nosy @rhettinger, @jdemeyer, @jdufresne
PRs
  • bpo-36743: __get__ is sometimes called without the owner argument #12992
  • [3.8] bpo-36743: __get__ is sometimes called without the owner argument (GH-12992) #15589
  • 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 = 'https://github.com/rhettinger'
    closed_at = <Date 2019-08-29.08:28:45.230>
    created_at = <Date 2019-04-27.17:56:43.412>
    labels = ['3.7', '3.8', 'docs']
    title = 'Docs: Descript __get__ signature defined differently across the docs'
    updated_at = <Date 2019-08-29.09:02:55.421>
    user = 'https://github.com/jdufresne'

    bugs.python.org fields:

    activity = <Date 2019-08-29.09:02:55.421>
    actor = 'rhettinger'
    assignee = 'rhettinger'
    closed = True
    closed_date = <Date 2019-08-29.08:28:45.230>
    closer = 'rhettinger'
    components = ['Documentation']
    creation = <Date 2019-04-27.17:56:43.412>
    creator = 'jdufresne'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 36743
    keywords = ['patch']
    message_count = 7.0
    messages = ['341004', '341023', '341333', '341334', '350708', '350755', '350759']
    nosy_count = 4.0
    nosy_names = ['rhettinger', 'docs@python', 'jdemeyer', 'jdufresne']
    pr_nums = ['12992', '15589']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = None
    url = 'https://bugs.python.org/issue36743'
    versions = ['Python 3.7', 'Python 3.8']

    Activity

    1. jdufresne commented on Apr 27, 2019

      jdufresnemannequin
      MannequinAuthor

      Here: https://docs.python.org/3/reference/datamodel.html#object.\_\_get__

      The __get__ signature is defined as:

      object.__get__(self, instance, owner)

      But here: https://docs.python.org/3/howto/descriptor.html#descriptor-protocol

      It is defined as:

      descr.__get__(self, obj, type=None)

      It is not clear to me as a reader if all descriptors should have the owner/type argument default to None or if it should be required. If it should default to None, I think all doc examples should follow this expectation to make it clear to someone implementing a descriptor for the first time. As best I can tell, the owner/type is always passed. So perhaps the =None shouldn't be there.

      Grepping the CPython code, I see lots of definitions for both required and optional, adding more confusion for me.

      If there is a definitive answer, I'm happy to follow through by updating the docs.

    2. rhettinger commented on Apr 28, 2019

      @rhettinger
      Contributor

      Perhaps the datamodel docs can be clarified to note that callers are allowed to omit the third argument (which usually only makes sense when the second argument is not None). FWIW, section 3.3.2.3 already has an example of a direct call to a __get__ descriptor with only the *obj* argument.

    3. jdemeyer commented on May 3, 2019

      @jdemeyer
      Contributor

      Personally, I have always found "instance" and "owner" very confusing names for these arguments. If you want to change the documentation, I would recommend changing those names too. Better names would be "obj" and "cls" or something like that.

    4. jdemeyer commented on May 3, 2019

      @jdemeyer
      Contributor

      Perhaps the datamodel docs can be clarified to note that callers are allowed to omit the third argument

      That's not true in general, only when __get__ is a slot wrapper (i.e. for classes implemented in C). When __get__ is a Python function, nothing special is done, it's just a Python function.

    5. rhettinger commented on Aug 29, 2019

      @rhettinger
      Contributor

      Collecting various factlets on this topic

      PEP-252 gives the specification for the descriptor protocol: "__get__(): a function callable with one or two arguments that retrieves the attribute value from an object."

      function_get, property_get, classmethod_get, and staticmethod_get all support calls with one or two arguments:

          >>> a = A()
          >>> class A:
                  def m(self):
                      return 42
                  @property
                  def p(self):
                      return 43
                  @classmethod
                  def c(cls):
                      return 44
                  @staticmethod
                  def s():
                      return 45
                  
          >>> a = A()
          >>> vars(A)['m'].__get__(a)()
          42
          >>> vars(A)['m'].__get__(a, A)()
          42
          >>> vars(A)['p'].__get__(a)
          43
          >>> vars(A)['p'].__get__(a, A)
          43
          >>> vars(A)['c'].__get__(a)()
          44
          >>> vars(A)['c'].__get__(a, A)()
          44
          >>> vars(A)['s'].__get__(a)()
          45
          >>> vars(A)['s'].__get__(a, A)()
          45

      Python functions that comply with the specification should also do the same (as taught by the descriptor HOWTO). That said, I have found multiple Python functions that aren't providing the None default. I will fix those as I find them.

      type.__getattribute__, object.__getattribute__ and super.__getattribute__ always call __get__ with both attributes specified:

          >>> class D:
                  def __get__(*args):
                      print(args)
      
          >>> class C:
                  d = D()
      
          >>> class S(C):
                  def f(self):
                      return super().d
              
          >>> C.d
          (<__main__.D object at 0x104d967f0>, None, <class '__main__.C'>)
          >>> C().d
          (<__main__.D object at 0x104d967f0>, <__main__.C object at 0x104df77c0>, <class '__main__.C'>)
          >>> S().f()
          (<__main__.D object at 0x104d967f0>, <__main__.S object at 0x104df7580>, <class '__main__.S'>
    6. rhettinger commented on Aug 29, 2019

      @rhettinger
      Contributor

      New changeset 0dac68f by Raymond Hettinger in branch 'master':
      bpo-36743: __get__ is sometimes called without the owner argument (bpo-12992)
      0dac68f

    7. rhettinger commented on Aug 29, 2019

      @rhettinger
      Contributor

      New changeset c71ae1a by Raymond Hettinger (Miss Islington (bot)) in branch '3.8':
      bpo-36743: __get__ is sometimes called without the owner argument (GH-12992) (GH-15589)
      c71ae1a

    8. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    3.7 (EOL)end of life3.8 (EOL)end of lifedocsDocumentation in the Doc dir

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions