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

AttributeError: '********' object has no attribute '*******' #198

Closed
s-nt-s opened this issue Oct 31, 2016 · 7 comments
Closed

AttributeError: '********' object has no attribute '*******' #198

s-nt-s opened this issue Oct 31, 2016 · 7 comments

Comments

@s-nt-s
Copy link

s-nt-s commented Oct 31, 2016

I have this code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import os
import sys
import rpyc
from rpyc.utils.server import ThreadedServer

import MyBot

class XmppService(rpyc.Service):
    def on_connect(self):
        # code that runs when a connection is created
        # (to init the serivce, if needed)
        pass

    def on_disconnect(self):
        # code that runs when the connection has already closed
        # (to finalize the service, if needed)
        pass

    def exposed_send_mensage(self, msg, to=None):
        self.exposed_bot.write_message(msg, to)

    def exposed_login(self):
        self.exposed_bot = MyBot.MyBot()
        self.exposed_bot.run()

if __name__ == "__main__":
    if len(sys.argv)>1:
        if sys.argv[1]=="login":
            c = rpyc.connect("localhost", 18861)
            c.root.login()
        else:
            c = rpyc.connect("localhost", 18861)
            c.root.send_mensage(" ".join(sys.argv[1:]))
    else:
        t = ThreadedServer(XmppService, port = 18861)
        t.start()

then:

  • I run python xmppservice.py and it is ok
  • I run python xmppservice.py login and it is ok
  • I run python xmppservice.py bla bla bla bla and then:
$ python xmppservice.py bla bla bla bla
Traceback (most recent call last):
  File "xmppservice.py", line 38, in <module>
    c.root.send_mensage(" ".join(sys.argv[1:]))
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/netref.py", line 196, in __call__
    return syncreq(_self, consts.HANDLE_CALL, args, kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/netref.py", line 71, in syncreq
    return conn.sync_request(handler, oid, *args)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 441, in sync_request
    raise obj
AttributeError: 'XmppService' object has no attribute 'exposed_bot'

========= Remote Traceback (1) =========
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 305, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 535, in _handle_call
    return self._local_objects[oid](*args, **dict(kwargs))
  File "xmppservice.py", line 25, in exposed_send_mensage
    self.exposed_bot.write_message(msg, to)
AttributeError: 'XmppService' object has no attribute 'exposed_bot'

And in the server side:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 305, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 535, in _handle_call
    return self._local_objects[oid](*args, **dict(kwargs))
  File "xmppservice.py", line 25, in exposed_send_mensage
    self.exposed_bot.write_message(msg, to)
AttributeError: 'XmppService' object has no attribute 'exposed_bot'
@aplicacionamedida
Copy link

aplicacionamedida commented Nov 18, 2016

Hi ,
I am not sure whether this is a bug. It seems that different connections have different namespaces. I tried this code and it works:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import os
import sys
import rpyc
from rpyc.utils.server import ThreadedServer

class MyBot(object):
    def run(self):
        print "running..."

    def write_message(self, msg, to):
        print "message to %s: %s"%(to, msg)

class XmppService(rpyc.Service):
    def on_connect(self):
        # code that runs when a connection is created
        # (to init the serivce, if needed)
        pass

    def on_disconnect(self):
        # code that runs when the connection has already closed
        # (to finalize the service, if needed)
        pass

    def exposed_send_mensage(self, msg, to=None):
        self.exposed_bot.write_message(msg, to)

    def exposed_login(self):
        self.exposed_bot = MyBot()
        self.exposed_bot.run()

if __name__ == "__main__":
    if len(sys.argv)>1:
        c = rpyc.connect("localhost", 18861)
        c.root.login()
        c.root.send_mensage(" ".join(sys.argv[2:]), sys.argv[1])
    else:
        t = ThreadedServer(XmppService, port = 18861)
        t.start()

I run this:

python xmppservice.py user bla bla bla

and I get this result:

running...
message to user: bla bla bla

@s-nt-s
Copy link
Author

s-nt-s commented Nov 18, 2016

Yes. That works because you do login and send message in the same execution, but what I was searching is a way to send a message using a previous connection. My final goal is to have a bot xmpp always connected as a daemon and that other script can ask it to send messages using the connection already open whiteout needing to do login again.

@aplicacionamedida
Copy link

aplicacionamedida commented Nov 18, 2016

You can do something like this (bot is now a global variable):

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import logging
import os
import sys
import rpyc
from rpyc.utils.server import ThreadedServer

class MyBot(object):
    def run(self):
        print "running..."

    def write_message(self, msg, to):
        print "message to %s: %s"%(to, msg)

class XmppService(rpyc.Service):
    def on_connect(self):
        # code that runs when a connection is created
        # (to init the serivce, if needed)
        pass

    def on_disconnect(self):
        # code that runs when the connection has already closed
        # (to finalize the service, if needed)
        pass

    def exposed_send_mensage(self, msg, to=None):
        bot.write_message(msg, to)

if __name__ == "__main__":
    if len(sys.argv)>1:
        c = rpyc.connect("localhost", 18861)
        c.root.send_mensage(" ".join(sys.argv[2:]), sys.argv[1])
    else:
        bot = MyBot()
        bot.run()
        t = ThreadedServer(XmppService, port = 18861)
        t.start()

@aplicacionamedida
Copy link

I think that another possibility is to create a custom service sharing the namespace to all connections.

I have an example in this recipe:
https://code.activestate.com/recipes/580721-tkinter-remote-debugging/

@s-nt-s
Copy link
Author

s-nt-s commented Nov 21, 2016

with bot as a global variable I get this:

Traceback (most recent call last):
  File "xmppservice2.py", line 32, in <module>
    c.root.send_mensage(" ".join(sys.argv[1:]))
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/netref.py", line 196, in __call__
    return syncreq(_self, consts.HANDLE_CALL, args, kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/netref.py", line 71, in syncreq
    return conn.sync_request(handler, oid, *args)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 441, in sync_request
    raise obj
NameError: global name 'bot' is not defined

========= Remote Traceback (1) =========
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 305, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py", line 535, in _handle_call
    return self._local_objects[oid](*args, **dict(kwargs))
  File "xmppservice2.py", line 23, in exposed_send_mensage
    bot.write_message(msg, to)
NameError: global name 'bot' is not defined

MyBot came from https://github.com/s-nt-s/XmppBot and the run method came from https://github.com/s-nt-s/XmppBot/blob/master/xmppbot.py#L155
maybe the problem is run method not finish?

@coldfix coldfix closed this as completed in 60792c1 Jul 9, 2017
@coldfix
Copy link
Contributor

coldfix commented Jul 9, 2017

As noted by @aplicacionamedida, this is not a bug. Each connection creates an independent service object. This fact is now documented in the tutorial.

Regarding your last post: Since you didn't show your code, I can only guess you didn't explicitly set global bot before setting bot = ... in the login method.

coldfix added a commit that referenced this issue Jul 26, 2017
- Add missing endpoints config in ThreadPoolServer (#222)
- Fix jython support (#156,#171)
- Improve documentation (#158,#185,#189,#198 and more)
coldfix added a commit that referenced this issue Dec 21, 2017
This is arguably the cleaner structure as it allows using a single
service for all clients and simplifies the structure (as was expected by
the user in #198).

BREAKS BACKWARD COMPATIBILITY:

- removing self._conn AND
- changing on_connect/on_disconnect signatures!
coldfix added a commit that referenced this issue Dec 21, 2017
This allows

- sharing the same service object among multiple clients on a server
  (as was the expectation by the user in #198)

- using an pre-initialized instance when connecting a client or hosting
  a oneshot server (partial resolution for #244)
coldfix added a commit that referenced this issue Dec 21, 2017
This allows

- sharing the same service object among multiple clients on a server
  (as was the expectation by the user in #198)

- using an pre-initialized instance when connecting a client or hosting
  a oneshot server (partial resolution for #244)
@coldfix
Copy link
Contributor

coldfix commented Dec 21, 2017

Note that in the next release (3.5) it will be possible to share the same service object for multiple connections by passing in a fully constructed service instance as the service argument.

coldfix added a commit that referenced this issue Jun 11, 2018
This release brings a few minor backward incompatibilities, so be sure to read
on before upgrading. However, fear not: the ones that are most likely relevant
to you have a relatively simple migration path.

Backward Incompatibilities
^^^^^^^^^^^^^^^^^^^^^^^^^^

* ``classic.teleport_function`` now executes the function in the connection's
  namespace by default. To get the old behaviour, use
  ``teleport_function(conn, func, conn.modules[func.__module__].__dict__)``
  instead.

* Changed signature of ``Service.on_connect`` and ``on_disconnect``, adding
  the connection as argument.

* Changed signature of ``Service.__init__``, removing the connection argument

* no longer store connection as ``self._conn``. (allows services that serve
  multiple clients using the same service object, see `#198`_).

* ``SlaveService`` is now split into two asymetric classes: ``SlaveService``
  and ``MasterService``. The slave exposes functionality to the master but can
  not anymore access remote objects on the master (`#232`_, `#248`_).
  If you were previously using ``SlaveService``, you may experience problems
  when feeding the slave with netrefs to objects on the master. In this case, do
  any of the following:

  * use ``ClassicService`` (acts exactly like the old ``SlaveService``)
  * use ``SlaveService`` with a ``config`` that allows attribute access etc
  * use ``rpyc.utils.deliver`` to feed copies rather than netrefs to
    the slave

* ``RegistryServer.on_service_removed`` is once again called whenever a service
  instance is removed, making it symmetric to ``on_service_added`` (`#238`_)
  This reverts PR `#173`_ on issue `#172`_.

* Removed module ``rpyc.experimental.splitbrain``. It's too confusing and
  undocumented for me and I won't be developing it, so better remove it
  altogether. (It's still available in the ``splitbrain`` branch)

* Removed module ``rpyc.experimental.retunnel``. Seemingly unused anywhere, no
  documentation, no clue what this is about.

* ``bin/rpyc_classic.py`` will bind to ``127.0.0.1`` instead of ``0.0.0.0`` by
  default

* ``SlaveService`` no longer serves exposed attributes (i.e., it now uses
  ``allow_exposed_attrs=False``)

* Exposed attributes no longer hide plain attributes if one otherwise has the
  required permissions to access the plain attribute. (`#165`_)

.. _#165: #165
.. _#172: #172
.. _#173: #173
.. _#198: #198
.. _#232: #232
.. _#238: #238
.. _#248: #248

What else is new
^^^^^^^^^^^^^^^^

* teleported functions will now be defined by default in the globals dict

* Can now explicitly specify globals for teleported functions

* Can now use streams as context manager

* keep a hard reference to connection in netrefs, may fix some ``EOFError``
  issues, in particular on Jython related (`#237`_)

* handle synchronous and asynchronous requests uniformly

* fix deadlock with connections talking to each other multithreadedly (`#270`_)

* handle timeouts cumulatively

* fix possible performance bug in ``Win32PipeStream.poll`` (oversleeping)

* use readthedocs theme for documentation (`#269`_)

* actually time out sync requests (`#264`_)

* clarify documentation concerning exceptions in ``Connection.ping`` (`#265`_)

* fix ``__hash__`` for netrefs (`#267`_, `#268`_)

* rename ``async`` module to ``async_`` for py37 compatibility (`#253`_)

* fix ``deliver()`` from IronPython to CPython2 (`#251`_)

* fix brine string handling in py2 IronPython (`#251`_)

* add gevent_ Server. For now, this requires using ``gevent.monkey.patch_all()``
  before importing for rpyc. Client connections can already be made without
  further changes to rpyc, just using gevent's monkey patching. (`#146`_)

* add function ``rpyc.lib.spawn`` to spawn daemon threads

* fix several bugs in ``bin/rpycd.py`` that crashed this script on startup
  (`#231`_)

* fix problem with MongoDB, or more generally any remote objects that have a
  *catch-all* ``__getattr__`` (`#165`_)

* fix bug when copying remote numpy arrays (`#236`_)

* added ``rpyc.utils.helpers.classpartial`` to bind arguments to services (`#244`_)

* can now pass services optionally as instance or class (could only pass as
  class, `#244`_)

* The service is now charged with setting up the connection, doing so in
  ``Service._connect``. This allows using custom protocols by e.g. subclassing
  ``Connection``.  More discussions and related features in `#239`_-`#247`_.

* service can now easily override protocol handlers, by updating
  ``conn._HANDLERS`` in ``_connect`` or ``on_connect``. For example:
  ``conn._HANDLERS[HANDLE_GETATTR] = self._handle_getattr``.

* most protocol handlers (``Connection._handle_XXX``) now directly get the
  object rather than its ID as first argument. This makes overriding
  individual handlers feel much more high-level. And by the way it turns out
  that this fixes two long-standing issues (`#137`_, `#153`_)

* fix bug with proxying context managers (`#228`_)

* expose server classes from ``rpyc`` top level module

* fix logger issue on jython

.. _#137: #137
.. _#146: #146
.. _#153: #153
.. _#165: #165
.. _#228: #228
.. _#231: #231
.. _#236: #236
.. _#237: #237
.. _#239: #239
.. _#244: #244
.. _#247: #247
.. _#251: #251
.. _#253: #253
.. _#264: #264
.. _#265: #265
.. _#267: #267
.. _#268: #268
.. _#269: #269
.. _#270: #270

.. _gevent: http://www.gevent.org/
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

No branches or pull requests

3 participants