Skip to content

Infinite recursion when logging from a property #6122

@jcsteh

Description

@jcsteh

STR:

  1. Open the NVDA Python console.

  2. Type the following:

    class C(object):
      @property
        def p(self):
          log.info("p", stack_info=True)
    
    C().p
    
  3. Look at the log.

You get something like this:

  File "<console>", line 4, in p
  File "C:\Python27\lib\logging\__init__.py", line 1159, in info
    self._log(INFO, msg, args, **kwargs)
  File "logHandler.py", line 91, in _log
    codepath=getCodePath(f)
  File "logHandler.py", line 55, in getCodePath
    attr=getattr(arg0,funcName)
  File "<console>", line 4, in p
  File "C:\Python27\lib\logging\__init__.py", line 1159, in info
    self._log(INFO, msg, args, **kwargs)
  File "logHandler.py", line 91, in _log
    codepath=getCodePath(f)
  File "logHandler.py", line 55, in getCodePath
    attr=getattr(arg0,funcName)
  File "<console>", line 4, in p
  File "C:\Python27\lib\logging\__init__.py", line 1159, in info
    self._log(INFO, msg, args, **kwargs)
  File "logHandler.py", line 91, in _log
    codepath=getCodePath(f)
  File "logHandler.py", line 55, in getCodePath
    attr=getattr(arg0,funcName)
...

And so the recursion continues. The reason is that logHandler.getCodePath retrieves the p attribute from the class, but in this case, the attribute is a property. Retrieving the property runs the logging code again... and thus begins the infinite recursion. I'm pretty sure we exceed maximum recursion depth, but getCodePath catches that exception.

As to why we aren't hitting this all the time, I think this only applies to properties which use decorators. In contrast, most of our properties use the _get_foo magic methods, and in that case, the function name from the code attribute will point at the _get_foo method, not the foo property itself. So, we've just been lucky enough not to log within a decorated property.

We should be able to solve this by trying to retrieve the attribute from the class first. If that attribute has a __get__ attribute, we know it's a descriptor (usually a property). If so, we shouldn't try to retrieve the attribute from the instance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    p2https://github.com/nvaccess/nvda/blob/master/projectDocs/issues/triage.md#priority

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions