Commit 0dac68f1 authored by Raymond Hettinger's avatar Raymond Hettinger Committed by GitHub

bpo-36743: __get__ is sometimes called without the owner argument (#12992)

parent 84125fed
...@@ -1618,21 +1618,32 @@ refers to the attribute whose name is the key of the property in the owner ...@@ -1618,21 +1618,32 @@ refers to the attribute whose name is the key of the property in the owner
class' :attr:`~object.__dict__`. class' :attr:`~object.__dict__`.
.. method:: object.__get__(self, instance, owner) .. method:: object.__get__(self, instance, owner=None)
Called to get the attribute of the owner class (class attribute access) or of an Called to get the attribute of the owner class (class attribute access) or
instance of that class (instance attribute access). *owner* is always the owner of an instance of that class (instance attribute access). The optional
class, while *instance* is the instance that the attribute was accessed through, *owner* argument is the owner class, while *instance* is the instance that
or ``None`` when the attribute is accessed through the *owner*. This method the attribute was accessed through, or ``None`` when the attribute is
should return the (computed) attribute value or raise an :exc:`AttributeError` accessed through the *owner*.
exception.
This method should return the computed attribute value or raise an
:exc:`AttributeError` exception.
:PEP:`252` specifies that :meth:`__get__` is callable with one or two
arguments. Python's own built-in descriptors support this specification;
however, it is likely that some third-party tools have descriptors
that require both arguments. Python's own :meth:`__getattribute__`
implementation always passes in both arguments whether they are required
or not.
.. method:: object.__set__(self, instance, value) .. method:: object.__set__(self, instance, value)
Called to set the attribute on an instance *instance* of the owner class to a Called to set the attribute on an instance *instance* of the owner class to a
new value, *value*. new value, *value*.
Note, adding :meth:`__set__` or :meth:`__delete__` changes the kind of
descriptor to a "data descriptor". See :ref:`descriptor-invocation` for
more details.
.. method:: object.__delete__(self, instance) .. method:: object.__delete__(self, instance)
......
...@@ -283,7 +283,7 @@ except AttributeError: ...@@ -283,7 +283,7 @@ except AttributeError:
class DocDescriptor: class DocDescriptor:
"""Helper for builtins.open.__doc__ """Helper for builtins.open.__doc__
""" """
def __get__(self, obj, typ): def __get__(self, obj, typ=None):
return ( return (
"open(file, mode='r', buffering=-1, encoding=None, " "open(file, mode='r', buffering=-1, encoding=None, "
"errors=None, newline=None, closefd=True)\n\n" + "errors=None, newline=None, closefd=True)\n\n" +
......
...@@ -383,7 +383,7 @@ class partialmethod(object): ...@@ -383,7 +383,7 @@ class partialmethod(object):
_method._partialmethod = self _method._partialmethod = self
return _method return _method
def __get__(self, obj, cls): def __get__(self, obj, cls=None):
get = getattr(self.func, "__get__", None) get = getattr(self.func, "__get__", None)
result = None result = None
if get is not None: if get is not None:
...@@ -888,7 +888,7 @@ class singledispatchmethod: ...@@ -888,7 +888,7 @@ class singledispatchmethod:
""" """
return self.dispatcher.register(cls, func=method) return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls): def __get__(self, obj, cls=None):
def _method(*args, **kwargs): def _method(*args, **kwargs):
method = self.dispatcher.dispatch(args[0].__class__) method = self.dispatcher.dispatch(args[0].__class__)
return method.__get__(obj, cls)(*args, **kwargs) return method.__get__(obj, cls)(*args, **kwargs)
...@@ -926,7 +926,7 @@ class cached_property: ...@@ -926,7 +926,7 @@ class cached_property:
f"({self.attrname!r} and {name!r})." f"({self.attrname!r} and {name!r})."
) )
def __get__(self, instance, owner): def __get__(self, instance, owner=None):
if instance is None: if instance is None:
return self return self
if self.attrname is None: if self.attrname is None:
......
...@@ -2804,7 +2804,7 @@ class PropertyMock(Mock): ...@@ -2804,7 +2804,7 @@ class PropertyMock(Mock):
def _get_child_mock(self, /, **kwargs): def _get_child_mock(self, /, **kwargs):
return MagicMock(**kwargs) return MagicMock(**kwargs)
def __get__(self, obj, obj_type): def __get__(self, obj, obj_type=None):
return self() return self()
def __set__(self, obj, val): def __set__(self, obj, val):
self(val) self(val)
......
...@@ -78,7 +78,7 @@ class EiffelDescriptor: ...@@ -78,7 +78,7 @@ class EiffelDescriptor:
self.__name__ = func.__name__ self.__name__ = func.__name__
self.__doc__ = func.__doc__ self.__doc__ = func.__doc__
def __get__(self, obj, cls): def __get__(self, obj, cls=None):
return EiffelMethodWrapper(obj, self) return EiffelMethodWrapper(obj, self)
def callmethod(self, inst, args, kwargs): def callmethod(self, inst, args, kwargs):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment