From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Thu, 29 Aug 2019 09:02:51 +0000 (-0700) Subject: bpo-36743: __get__ is sometimes called without the owner argument (GH-12992) (GH... X-Git-Tag: v3.8.0b4~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c71ae1a45bd6e6d0f5aebc470b35f5a7dc0d8078;p=python bpo-36743: __get__ is sometimes called without the owner argument (GH-12992) (GH-15589) (cherry picked from commit 0dac68f1e593c11612ed54af9edb865d398f3b05) Co-authored-by: Raymond Hettinger --- diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst index d1702ccb64..8813f57587 100644 --- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1618,21 +1618,32 @@ refers to the attribute whose name is the key of the property in the owner 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 - instance of that class (instance attribute access). *owner* is always the owner - class, while *instance* is the instance that the attribute was accessed through, - or ``None`` when the attribute is accessed through the *owner*. This method - should return the (computed) attribute value or raise an :exc:`AttributeError` - exception. + Called to get the attribute of the owner class (class attribute access) or + of an instance of that class (instance attribute access). The optional + *owner* argument is the owner class, while *instance* is the instance that + the attribute was accessed through, or ``None`` when the attribute is + accessed through the *owner*. + 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) Called to set the attribute on an instance *instance* of the owner class to a 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) diff --git a/Lib/_pyio.py b/Lib/_pyio.py index 650109295f..eb4e6620fc 100644 --- a/Lib/_pyio.py +++ b/Lib/_pyio.py @@ -281,7 +281,7 @@ except AttributeError: class DocDescriptor: """Helper for builtins.open.__doc__ """ - def __get__(self, obj, typ): + def __get__(self, obj, typ=None): return ( "open(file, mode='r', buffering=-1, encoding=None, " "errors=None, newline=None, closefd=True)\n\n" + diff --git a/Lib/functools.py b/Lib/functools.py index 64d120182b..a674a685df 100644 --- a/Lib/functools.py +++ b/Lib/functools.py @@ -400,7 +400,7 @@ class partialmethod(object): _method._partialmethod = self return _method - def __get__(self, obj, cls): + def __get__(self, obj, cls=None): get = getattr(self.func, "__get__", None) result = None if get is not None: @@ -905,7 +905,7 @@ class singledispatchmethod: """ return self.dispatcher.register(cls, func=method) - def __get__(self, obj, cls): + def __get__(self, obj, cls=None): def _method(*args, **kwargs): method = self.dispatcher.dispatch(args[0].__class__) return method.__get__(obj, cls)(*args, **kwargs) @@ -943,7 +943,7 @@ class cached_property: f"({self.attrname!r} and {name!r})." ) - def __get__(self, instance, owner): + def __get__(self, instance, owner=None): if instance is None: return self if self.attrname is None: diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 7ab6812591..4c76f53f38 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -2806,7 +2806,7 @@ class PropertyMock(Mock): def _get_child_mock(self, /, **kwargs): return MagicMock(**kwargs) - def __get__(self, obj, obj_type): + def __get__(self, obj, obj_type=None): return self() def __set__(self, obj, val): self(val) diff --git a/Tools/demo/eiffel.py b/Tools/demo/eiffel.py index 736abea817..a76c2324dd 100755 --- a/Tools/demo/eiffel.py +++ b/Tools/demo/eiffel.py @@ -78,7 +78,7 @@ class EiffelDescriptor: self.__name__ = func.__name__ self.__doc__ = func.__doc__ - def __get__(self, obj, cls): + def __get__(self, obj, cls=None): return EiffelMethodWrapper(obj, self) def callmethod(self, inst, args, kwargs):