Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposed descriptors raising AttributeErrors cause different remote tracebacks when accessing as exposed_foo vs. foo #478

Closed
gschaffner opened this issue Feb 27, 2022 · 0 comments · Fixed by #479
Assignees

Comments

@gschaffner
Copy link
Contributor

An AttributeError raised in the __get__ of an exposed descriptor exposed_foo is only propagated correctly to the client if the client accesses the exposed descriptor as conn.root.exposed_foo rather than as conn.root.foo. See example below :)

Environment
  • RPyC version: 5.1.0
  • Python version: CPython 3.10.2
  • operating system: Manjaro
Minimal example

Server:

import rpyc


class Foo:
    def __get__(self, owner, owner_type=None):
        raise AttributeError("This string doesn't always reach the client's traceback!")


class MWEService(rpyc.Service):
    exposed_foo = Foo()


if __name__ == "__main__":
    from rpyc.utils.server import ThreadedServer

    svr = ThreadedServer(MWEService, port=18861)
    svr.start()

Client:

import rpyc

conn = rpyc.connect("localhost", port=18861)

# The following two lines will raise different exceptions!
conn.root.exposed_foo
# conn.root.foo
Tracebacks

When using conn.root.exposed_foo:

Traceback (most recent call last):
  File "/tmp/tmp.YPumucnVwE/mwe_c.py", line 6, in <module>
    conn.root.exposed_foo
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/netref.py", line 153, in __getattr__
    return syncreq(self, consts.HANDLE_GETATTR, name)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/netref.py", line 63, in syncreq
    return conn.sync_request(handler, proxy, *args)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 474, in sync_request
    return self.async_request(handler, *args, timeout=timeout).value
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/async_.py", line 103, in value
    raise self._obj
_get_exception_class.<locals>.Derived: This string doesn't always reach the client's traceback!

========= Remote Traceback (1) =========
Traceback (most recent call last):
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 324, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 609, in _handle_getattr
    return self._access_attr(obj, name, (), "_rpyc_getattr", "allow_getattr", getattr)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 537, in _access_attr
    return accessor(obj, name, *args)
  File "/tmp/tmp.YPumucnVwE/mwe_s.py", line 6, in __get__
    raise AttributeError("This string doesn't always reach the client's traceback!")
AttributeError: This string doesn't always reach the client's traceback!

When using conn.root.foo:

Traceback (most recent call last):
  File "/tmp/tmp.YPumucnVwE/mwe_c.py", line 7, in <module>
    conn.root.foo
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/netref.py", line 153, in __getattr__
    return syncreq(self, consts.HANDLE_GETATTR, name)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/netref.py", line 63, in syncreq
    return conn.sync_request(handler, proxy, *args)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 474, in sync_request
    return self.async_request(handler, *args, timeout=timeout).value
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/async_.py", line 103, in value
    raise self._obj
_get_exception_class.<locals>.Derived: cannot access 'foo'

========= Remote Traceback (1) =========
Traceback (most recent call last):
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 324, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 609, in _handle_getattr
    return self._access_attr(obj, name, (), "_rpyc_getattr", "allow_getattr", getattr)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 536, in _access_attr
    name = self._check_attr(obj, name, param)
  File "/tmp/tmp.YPumucnVwE/.venv/lib/python3.10/site-packages/rpyc/core/protocol.py", line 526, in _check_attr
    raise AttributeError(f"cannot access {name!r}")
AttributeError: cannot access 'foo'
Debugging

This appears to occur only when __get__ raises an AttributeError in particular, which makes sense. If __get__ raises an exception of a different type, it is raised by hasattr here:

has_exposed = prefix and hasattr(obj, prefix + name)

But if __get__ raises an AttributeError, this hasattr returns False instead of raising, and so the arguments of the AttributeError are lost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants