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

Trouble accessing remote numpy objects #236

Closed
joshStillerman opened this issue Nov 3, 2017 · 6 comments
Closed

Trouble accessing remote numpy objects #236

joshStillerman opened this issue Nov 3, 2017 · 6 comments

Comments

@joshStillerman
Copy link

joshStillerman commented Nov 3, 2017

Using Python 2.7
Numpy '1.7.1'
rpyc (3, 4, 4)

I am using rpyc.classic.connect
I also tried using rpyc.utils.zerodeploy, with the same results.

I have a remote object d

Help on ndarray in module numpy object:

class ndarray(rpyc.core.netref.BaseNetref)
 |  Method resolution order:
 |      ndarray
 |      rpyc.core.netref.BaseNetref
 |      __builtin__.object
 |  
 |  Methods defined here:
 |  
 |  __abs__(_self, *args, **kwargs)
 |      x.__abs__() <==> abs(x)
 |  
 |  __add__(_self, *args, **kwargs)
...

If I try to plot it, or make a local numpy array from it I get an error:

>>> dd = np.array(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid __array_struct__
>>> 

I can get it to segfault by typing: (actually this time it choked on a double free)

>>> help(d.__array_struct__)
*** Error in `python': double free or corruption (!prev): 0x0000000001a44920 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7c619)[0x7f8e0cc70619]
/lib64/libpython2.7.so.1.0(+0x9da63)[0x7f8e0d979a63]
/lib64/libpython2.7.so.1.0(+0x6f282)[0x7f8e0d94b282]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x67d9)[0x7f8e0d9c0599]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x663c)[0x7f8e0d9c03fc]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x67bd)[0x7f8e0d9c057d]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x67bd)[0x7f8e0d9c057d]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x67bd)[0x7f8e0d9c057d]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(+0x70858)[0x7f8e0d94c858]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x17fd)[0x7f8e0d9bb5bd]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x663c)[0x7f8e0d9c03fc]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x67bd)[0x7f8e0d9c057d]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(+0x70858)[0x7f8e0d94c858]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(+0x5a995)[0x7f8e0d936995]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(PyObject_CallFunctionObjArgs+0xbc)[0x7f8e0d92829c]
/lib64/libpython2.7.so.1.0(+0x9e2bf)[0x7f8e0d97a2bf]
/lib64/libpython2.7.so.1.0(+0xa5183)[0x7f8e0d981183]
/lib64/libpython2.7.so.1.0(+0x484fe)[0x7f8e0d9244fe]
/lib64/libpython2.7.so.1.0(+0xa0a72)[0x7f8e0d97ca72]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(PyObject_CallFunctionObjArgs+0xbc)[0x7f8e0d92829c]
/lib64/libpython2.7.so.1.0(PyObject_IsInstance+0x12f)[0x7f8e0d92889f]
/lib64/libpython2.7.so.1.0(+0xd9b3a)[0x7f8e0d9b5b3a]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x6df0)[0x7f8e0d9c0bb0]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x663c)[0x7f8e0d9c03fc]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x663c)[0x7f8e0d9c03fc]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x663c)[0x7f8e0d9c03fc]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x67bd)[0x7f8e0d9c057d]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(+0x7094d)[0x7f8e0d94c94d]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(+0x5a995)[0x7f8e0d936995]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(+0x56d38)[0x7f8e0d932d38]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x17fd)[0x7f8e0d9bb5bd]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(+0x70858)[0x7f8e0d94c858]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(+0x5a995)[0x7f8e0d936995]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(+0xa2ce7)[0x7f8e0d97ece7]
/lib64/libpython2.7.so.1.0(PyObject_Call+0x43)[0x7f8e0d9279a3]
/lib64/libpython2.7.so.1.0(PyEval_EvalFrameEx+0x2336)[0x7f8e0d9bc0f6]
/lib64/libpython2.7.so.1.0(PyEval_EvalCodeEx+0x7ed)[0x7f8e0d9c2efd]
/lib64/libpython2.7.so.1.0(PyEval_EvalCode+0x32)[0x7f8e0d9c3002]
/lib64/libpython2.7.so.1.0(+0x10043f)[0x7f8e0d9dc43f]
/lib64/libpython2.7.so.1.0(PyRun_InteractiveOneFlags+0x150)[0x7f8e0d9de4f0]
/lib64/libpython2.7.so.1.0(PyRun_InteractiveLoopFlags+0x5e)[0x7f8e0d9de6de]
/lib64/libpython2.7.so.1.0(PyRun_AnyFileExFlags+0x3e)[0x7f8e0d9ded6e]
/lib64/libpython2.7.so.1.0(Py_Main+0xc9f)[0x7f8e0d9efa3f]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f8e0cc15c05]
python[0x40071e]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:00 178051                             /usr/bin/python2.7
00600000-00601000 r--p 00000000 fd:00 178051                             /usr/bin/python2.7
00601000-00602000 rw-p 00001000 fd:00 178051                             /usr/bin/python2.7
00d7b000-01c21000 rw-p 00000000 00:00 0                                  [heap]
7f8df4000000-7f8df4021000 rw-p 00000000 00:00 0 
7f8df4021000-7f8df8000000 ---p 00000000 00:00 0 
7f8dfb6ff000-7f8dfb70e000 r-xp 00000000 fd:00 33603078                   /usr/lib64/libbz2.so.1.0.6
7f8dfb70e000-7f8dfb90d000 ---p 0000f000 fd:00 33603078                   /usr/lib64/libbz2.so.1.0.6
7f8dfb90d000-7f8dfb90e000 r--p 0000e000 fd:00 33603078                   /usr/lib64/libbz2.so.1.0.6
7f8dfb90e000-7f8dfb90f000 rw-p 0000f000 fd:00 33603078                   /usr/lib64/libbz2.so.1.0.6
7f8dfb90f000-7f8dfb926000 r-xp 00000000 fd:00 33603209                   /usr/lib64/libelf-0.168.so
7f8dfb926000-7f8dfbb25000 ---p 00017000 fd:00 33603209                   /usr/lib64/libelf-0.168.so
7f8dfbb25000-7f8dfbb26000 r--p 00016000 fd:00 33603209                   /usr/lib64/libelf-0.168.so
7f8dfbb26000-7f8dfbb27000 rw-p 00017000 fd:00 33603209                   /usr/lib64/libelf-0.168.so
7f8dfbb27000-7f8dfbb2b000 r-xp 00000000 fd:00 33603259                   /usr/lib64/libattr.so.1.1.0
7f8dfbb2b000-7f8dfbd2a000 ---p 00004000 fd:00 33603259                   /usr/lib64/libattr.so.1.1.0
7f8dfbd2a000-7f8dfbd2b000 r--p 00003000 fd:00 33603259                   /usr/lib64/libattr.so.1.1.0
7f8dfbd2b000-7f8dfbd2c000 rw-p 00004000 fd:00 33603259                   /usr/lib64/libattr.so.1.1.0
7f8dfbd2c000-7f8dfbd70000 r-xp 00000000 fd:00 33721523                   /usr/lib64/libdw-0.168.so
7f8dfbd70000-7f8dfbf70000 ---p 00044000 fd:00 33721523                   /usr/lib64/libdw-0.168.so
7f8dfbf70000-7f8dfbf72000 r--p 00044000 fd:00 33721523                   /usr/lib64/libdw-0.168.so
7f8dfbf72000-7f8dfbf73000 rw-p 00046000 fd:00 33721523                   /usr/lib64/libdw-0.168.so
7f8dfbf73000-7f8dfbf77000 r-xp 00000000 fd:00 33603115                   /usr/lib64/libgpg-error.so.0.10.0
7f8dfbf77000-7f8dfc176000 ---p 00004000 fd:00 33603115                   /usr/lib64/libgpg-error.so.0.10.0
7f8dfc176000-7f8dfc177000 r--p 00003000 fd:00 33603115                   /usr/lib64/libgpg-error.so.0.10.0
7f8dfc177000-7f8dfc178000 rw-p 00004000 fd:00 33603115                   /usr/lib64/libgpg-error.so.0.10.0
7f8dfc178000-7f8dfc1f5000 r-xp 00000000 fd:00 33603133                   /usr/lib64/libgcrypt.so.11.8.2
7f8dfc1f5000-7f8dfc3f4000 ---p 0007d000 fd:00 33603133                   /usr/lib64/libgcrypt.so.11.8.2
7f8dfc3f4000-7f8dfc3f5000 r--p 0007c000 fd:00 33603133                   /usr/lib64/libgcrypt.so.11.8.2
7f8dfc3f5000-7f8dfc3f8000 rw-p 0007d000 fd:00 33603133                   /usr/lib64/libgcrypt.so.11.8.2
7f8dfc3f8000-7f8dfc3f9000 rw-p 00000000 00:00 0 Aborted (core dumped)
@coldfix
Copy link
Contributor

coldfix commented Nov 7, 2017

Hi,

sorry for the late response. I can reproduce the ValueError: invalid __array_struct__, however not the segfault.

netref objects don't work as direct substitutes in all cases. For objects like numpy arrays, it is probably best to x = rpyc.classic.obtain(d) before working with the object.

Admittedly, it would be nicer to have it working better by default, and it might be possible to improve by adding a __array_struct__ magic method to the BaseNetref class in rpyc/core/netref.py. If you have fun and time, you may want to try this.

For reference, the minimal working example is:

  • start ./bin/rpyc_classic.py --host 127.0.0.1 --port 18812
  • connect the following client:
import numpy as np
import rpyc
c = rpyc.classic.connect('localhost', 18812)
d = c.modules['numpy'].array([1, 2, 3])

x = np.array(d)  # ValueError

help(d.__array_struct__)  # see below

The last line seems to work fine from a client perspective, but shows the following exception on the server:

Traceback (most recent call last):
  File "/home/thomas/dev/project/rpyc/rpyc/core/protocol.py", line 347, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "/home/thomas/dev/project/rpyc/rpyc/core/protocol.py", line 630, in _handle_getattr
    return self._access_attr(oid, name, (), "_rpyc_getattr", "allow_getattr", getattr)
  File "/home/thomas/dev/project/rpyc/rpyc/core/protocol.py", line 596, in _access_attr
    return accessor(obj, name, *args)
AttributeError: 'PyCapsule' object has no attribute '__name__'

@joshStillerman
Copy link
Author

I really appreciate your response! Thanks!

I don't think adding array_struct to the rpyc/core/netref will work because the help on the object says it will resolve methods in this order:
| ndarray
| rpyc.core.netref.BaseNetref
so ndarray's array_struct will supercede any that I would define.

@coldfix
Copy link
Contributor

coldfix commented Dec 22, 2017

FYI, I made np.array(d) work now (bit of a hack though...). This will be part of the next release. Give me a call, if problems still persist (I didn't check matplotlib). However, the recommended method should be using rpyc.classic.obtain(d).

Best, Thomas

@joshStillerman
Copy link
Author

joshStillerman commented Dec 22, 2017 via email

@pilcru
Copy link

pilcru commented Feb 16, 2018

Hi,

For some reason, I can't get your example above to work, with the latest from master. On line 4, I get;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\users\pcuvil.win\code\rpyc\rpyc\core\netref.py", line 206, in __call__
    return syncreq(_self, consts.HANDLE_CALL, args, kwargs)
  File "c:\users\pcuvil.win\code\rpyc\rpyc\core\netref.py", line 78, in syncreq
    return conn.sync_request(handler, proxy, *args)
  File "c:\users\pcuvil.win\code\rpyc\rpyc\core\protocol.py", line 516, in sync_request
    raise obj
ValueError: object __array__ method not producing an array

========= Remote Traceback (1) =========
Traceback (most recent call last):
  File "c:\users\pcuvil.win\code\rpyc\rpyc\core\protocol.py", line 342, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "c:\users\pcuvil.win\code\rpyc\rpyc\core\protocol.py", line 638, in _handle_call
    return obj(*args, **dict(kwargs))
ValueError: object __array__ method not producing an array

That's on Python 2 and 3. I think the issue is that this only works when the remote object is already a ndarray. Casting with numpy.asarray before returning will solve the issue, but it does not make a lot of sense if the __array__ attribute was from a package other than numpy...

pilcru added a commit to pilcru/rpyc that referenced this issue Feb 16, 2018
The remote object needs to be an array already for __array__() to be accepted by Numpy.
@coldfix
Copy link
Contributor

coldfix commented Feb 19, 2018

Hi, thanks for the info! (I wonder why it was working for me when I tested).

Please feel free to submit your commit as pull-request (after retabbing to spaces!).

pilcru added a commit to pilcru/rpyc that referenced this issue Feb 19, 2018
The remote object needs to be an array already for __array__() to be accepted by Numpy.
pilcru added a commit to pilcru/rpyc that referenced this issue Feb 19, 2018
The remote object needs to be an array already for __array__() to be accepted by Numpy.
coldfix added a commit that referenced this issue Mar 2, 2018
Resolves #236

Closes #256, cleaner solution (?)
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