classification
Title: Documentation for __getattr__
Type: enhancement Stage: resolved
Components: Documentation, Interpreter Core Versions: Python 3.8, Python 3.7, Python 3.6
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Paul.Davis, cheryl.sabella, docs@python, dstanek, eric.araujo, ncoghlan, terry.reedy
Priority: normal Keywords: needs review, patch

Created on 2010-05-15 04:10 by Paul.Davis, last changed 2018-02-05 03:27 by ncoghlan. This issue is now closed.

Files
File name Uploaded Description Edit
example.py Paul.Davis, 2010-05-15 04:10 Short example
Pull Requests
URL Status Linked Edit
PR 4754 merged cheryl.sabella, 2017-12-08 01:25
PR 5542 merged miss-islington, 2018-02-05 02:41
PR 5543 merged miss-islington, 2018-02-05 02:42
Messages (16)
msg105790 - (view) Author: Paul Davis (Paul.Davis) Date: 2010-05-15 04:10
The docs for __getattr__ in the object model section could be more specific on the behavior when a @property raises an AttributeError and there is a custom __getattr__ defined. Specifically, it wasn't exactly clear that __getattr__ would be invoked after a @property was found and evaluated.

The attached script demonstrates the issue on OS X 10.6, Python 2.6.1

I'm thinking something along the lines of:

If the attribute search encounters an AttributeError (perhaps due to a @property raising the error) the search is considered a failure and __getattr__ is invoked.
msg105791 - (view) Author: Paul Davis (Paul.Davis) Date: 2010-05-15 04:13
I should mention, in example.py, it wasn't immediately clear that "print f.bing" would actually print instead of raising the AttributeError. As in, I had a property raising an unexpected error that happend to be an AttributeError. If its not an attribute error, the error is not swallowed. I can see the obvious argument for "AttributeError means not found", it just wasn't immediately obvious what was going on.
msg113192 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2010-08-07 19:45
The problem with changing 2.7 docs is that object access is different for old- and new-style properties. Does your example work if you remove 'object'? (IE, can old style classes have properties?)

For new-style classes, the example behavior is clear if you 1. know that object has a .__getattribute__ method inherited by everything when not overriden and 2. read the doc for that which says that __getattr__ is called whenever a __getattribute__ call raises AttributeError, which it does here by passing through the .get error.

For 3.x, I think in 3.3.2. Customizing attribute access,
object.__getattr__(self, name) 
"Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. "

might be replaced by

"Called when self.__getattribute__(name) raise AttributeError because name is not an instance attribute, not found in the class tree for self, or is a property attribute whose .get() method raises AttributeError."

But this does not work for 2.7.
msg113193 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2010-08-07 19:50
/raise/raises/

I am pretty sure that when __getattribute__ is bypassed, so is __getattr__.
msg113212 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-08-07 21:36
Old-style classes can’t have descriptors, hence no properties, static methods, class methods or super.
msg307831 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-12-08 02:12
Cheryl, thank you for reviving this, as it is still needed.  A slightly revised example better illustrates the claim in the doc revision about when __getattr__ is called.

class Foo(object):

    def __init__(self):
        self.foo = 1
        self.data = {"bing": 4}

    def __getattr__(self, name):
        print(f'Getting {name}')
        return self.data.get(name)

    @property
    def bar(self):
        return 3

    @property
    def bing(self):
        raise AttributeError("blarg")

f = Foo()
print('foo', f.foo)
print('__str__', f.__str__)
print('bar', f.bar)
print('bing', f.bing)
f.__getattribute__('bing')

# prints
foo 1
__str__ <method-wrapper '__str__' of Foo object at 0x0000016712378128>
bar 3
Getting bing
bing 4
Traceback (most recent call last):
  File "F:\Python\a\tem2.py", line 24, in <module>
    f.__getattribute__('bing')
  File "F:\Python\a\tem2.py", line 17, in bing
    raise AttributeError("blarg")
AttributeError: blarg
msg307853 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2017-12-08 16:32
Terry,

Thanks for clarifying with this example.  I hadn't tried this when I was playing with the other example.  I guess __getattribute__ might be defined by a class, but generally wouldn't be called directly, so the use of __getattr__ and __getattribute__ and the raising of AttributeError is more for an `attributeref` (https://docs.python.org/3/reference/expressions.html#attribute-references) usage?
msg307876 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-12-09 02:27
Before testing, let alone documenting, the status quo, I would like to be sure that suppressing the exception is truly the intended behavior.  Is there a way to get an annotated listing from git (given which patch, and therefore which person, is responsible for each line)?  I will try asking on pydev.

Calling __getattr__ on property failure is a behavior of __getattribute__, not of the property, and I would expect object.__getattribute__ to be tested wherever object is, but I have not found such tests.  If we do add a test, the best model in test_desc.py looks like `def test_module_subclasses(self):`.  The test class would only need __getattr__ and the faulty property.

class Foo(object):
    def __getattr__(self, name):
        print(f'Getattr {name}')
        return True
    @property
    def bing(self):
        print('Property bing')
        raise AttributeError("blarg")

f = Foo()
print(f.bing)

#prints (which would be the log list in a test)
Property bing
Getattr bing
True
msg307877 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-12-09 02:29
The behavior and doc for __setattr__ and __delattr__ should also be checked.
msg307894 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2017-12-09 12:25
>> Is there a way to get an annotated listing from git (given which patch, and therefore which person, is responsible for each line)?

Which source did you want to look at?  In github, if you go into any source, you can click on a line and it gives an option for 'git blame'.  That shows the last commit change for each line.  You can then click an icon to see a previous commit, etc.  For the .rst sources, it's a little different and there is a Blame button at the top of the source that will bring up the same view (commit annotations to the left of the source) as right-clicking.

I had posted about git blame a few months ago on core mentorship and Carol Willing mentioned another tool to get all the changes by line.  Here was her post:

Thanks for passing along the tip for others. You may also find the npm package `git-guilt` useful as it will display all the contributors to a particular line's history. https://www.npmjs.com/package/git-guilt <https://www.npmjs.com/package/git-guilt>
msg307907 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-12-09 18:12
Thanks.  I normally look at source in my local clone with an editor.  I found 'view blame' and 'view blame prior' on github.
msg311535 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2018-02-03 05:03
Nick, this is about better documenting the behavior of __get(set/del)attr__ in 3.x it relations to AttributeError in a property.  I think I understand what it does and think the patch is correct.  Could you either review or suggest someone else who better understands core behavior like this?
msg311633 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2018-02-05 02:03
New changeset d1f318105b8781b01f3507d5cb0fd841b977d5f2 by Nick Coghlan (Cheryl Sabella) in branch 'master':
bpo-8722: Document __getattr__ behavior with AttributeError in property (GH-4754)
https://github.com/python/cpython/commit/d1f318105b8781b01f3507d5cb0fd841b977d5f2
msg311637 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2018-02-05 03:10
New changeset a8c25d1c7f0d395861cc3e10dd01989150891c95 by Nick Coghlan (Miss Islington (bot)) in branch '3.6':
[3.6] bpo-8722: Document __getattr__ behavior with AttributeError in property (GH-5542)
https://github.com/python/cpython/commit/a8c25d1c7f0d395861cc3e10dd01989150891c95
msg311638 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2018-02-05 03:16
New changeset fea0a12f6bee4a36b2c9533003e33a12c58d2d91 by Nick Coghlan (Miss Islington (bot)) in branch '3.7':
[3.7] bpo-8722: Document __getattr__ behavior with AttributeError in property (GH-5543)
https://github.com/python/cpython/commit/fea0a12f6bee4a36b2c9533003e33a12c58d2d91
msg311640 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2018-02-05 03:27
Thanks for the patch Cheryl, and for the reviews Terry!
History
Date User Action Args
2018-02-05 03:27:03ncoghlansetstatus: open -> closed
resolution: fixed
messages: + msg311640

stage: patch review -> resolved
2018-02-05 03:16:51ncoghlansetmessages: + msg311638
2018-02-05 03:10:03ncoghlansetmessages: + msg311637
2018-02-05 02:42:30miss-islingtonsetpull_requests: + pull_request5369
2018-02-05 02:41:28miss-islingtonsetkeywords: + patch
pull_requests: + pull_request5368
2018-02-05 02:03:25ncoghlansetmessages: + msg311633
2018-02-03 05:03:04terry.reedysetversions: + Python 3.8
nosy: + ncoghlan

messages: + msg311535

components: + Interpreter Core
2017-12-09 18:12:31terry.reedysetkeywords: - patch

messages: + msg307907
2017-12-09 12:25:16cheryl.sabellasetmessages: + msg307894
2017-12-09 09:30:13berker.peksagsetkeywords: + patch
2017-12-09 02:29:44terry.reedysetmessages: + msg307877
2017-12-09 02:27:28terry.reedysetmessages: + msg307876
2017-12-08 16:32:03cheryl.sabellasetnosy: + cheryl.sabella
messages: + msg307853
2017-12-08 02:12:48terry.reedysetmessages: + msg307831
2017-12-08 01:27:13cheryl.sabellasetkeywords: + needs review, - patch
versions: + Python 3.6, Python 3.7, - Python 3.1, Python 2.7, Python 3.2, Python 3.3
2017-12-08 01:25:20cheryl.sabellasetstage: needs patch -> patch review
pull_requests: + pull_request4657
2011-03-09 02:39:38terry.reedysetnosy: terry.reedy, dstanek, eric.araujo, docs@python, Paul.Davis
versions: + Python 3.3
2010-08-18 00:14:47dstaneksetnosy: + dstanek
2010-08-07 21:36:07eric.araujosetnosy: + eric.araujo
messages: + msg113212
2010-08-07 19:50:06terry.reedysetmessages: + msg113193
2010-08-07 19:45:18terry.reedysetversions: - Python 2.6
nosy: + terry.reedy

messages: + msg113192

keywords: + patch
stage: needs patch
2010-07-11 02:18:07terry.reedysetversions: + Python 3.1, Python 2.7, Python 3.2
2010-05-15 04:13:32Paul.Davissetmessages: + msg105791
2010-05-15 04:10:57Paul.Daviscreate