Ulm-IQO / qudi-core

A framework for modular measurement applications.
GNU General Public License v3.0
28 stars 21 forks source link

[Bug] TypeError occurs in ipython console, when try to get enum type return. #19

Closed kiotex closed 2 years ago

kiotex commented 2 years ago

Version

current main

What is affected by the bug?

When I run a command that returns with an enum type such as on() in simple_laser_dummy.py in qudi-iqo-modules on the ipython console, I get a type error.

In[1]: laser_dummy.on()
Out[1]:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File ~\anaconda3\envs\qudi-env\lib\site-packages\IPython\core\formatters.py:335, in BaseFormatter.__call__(self, obj)
    332 if self.enabled:
    333     # lookup registered printer
    334     try:
--> 335         printer = self.lookup(obj)
    336     except KeyError:
    337         pass

File ~\anaconda3\envs\qudi-env\lib\site-packages\IPython\core\formatters.py:393, in BaseFormatter.lookup(self, obj)
    391     return self.singleton_printers[obj_id]
    392 # then lookup by type
--> 393 return self.lookup_by_type(_get_type(obj))

File ~\anaconda3\envs\qudi-env\lib\site-packages\IPython\core\formatters.py:274, in _get_type(obj)
    272 def _get_type(obj):
    273     """Return the type of an instance (old and new-style)"""
--> 274     return getattr(obj, '__class__', None) or type(obj)

File ~\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\netref.py:261, in _make_method.<locals>.method(_self, *args, **kwargs)
    259 def method(_self, *args, **kwargs):
    260     kwargs = tuple(kwargs.items())
--> 261     return syncreq(_self, consts.HANDLE_CALLATTR, name, args, kwargs)

File ~\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\netref.py:63, in syncreq(proxy, handler, *args)
     51 """Performs a synchronous request on the given proxy object.
     52 Not intended to be invoked directly.
     53 
   (...)
     60 :returns: the result of the operation
     61 """
     62 conn = object.__getattribute__(proxy, "____conn__")
---> 63 return conn.sync_request(handler, proxy, *args)

File ~\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\protocol.py:473, in Connection.sync_request(self, handler, *args)
    467 """requests, sends a synchronous request (waits for the reply to arrive)
    468 
    469 :raises: any exception that the requets may be generated
    470 :returns: the result of the request
    471 """
    472 timeout = self._config["sync_request_timeout"]
--> 473 return self.async_request(handler, *args, timeout=timeout).value

File ~\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\async_.py:102, in AsyncResult.value(self)
    100 self.wait()
    101 if self._is_exc:
--> 102     raise self._obj
    103 else:
    104     return self._obj

TypeError: descriptor '__bool__' of 'int' object needs an argument

========= Remote Traceback (1) =========
Traceback (most recent call last):
  File "C:\Users\MY_NAME\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\protocol.py", line 324, in _dispatch_request
    res = self._HANDLERS[handler](self, *args)
  File "C:\Users\MY_NAME\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\protocol.py", line 618, in _handle_callattr
    return self._handle_call(obj, args, kwargs)
  File "C:\Users\MY_NAME\anaconda3\envs\qudi-env\lib\site-packages\rpyc\core\protocol.py", line 592, in _handle_call
    return obj(*args, **dict(kwargs))
TypeError: descriptor '__bool__' of 'int' object needs an argument

This error message was displayed repeatedly.

When does the bug occur?

load simple_laser_dummy.py in qudi-iqo-modules modules in qudi. Execute a on() command.

Infometion of environment

In[10]: import platform, sys, IPython, rpyc

In[11]: platform.platform()
Out[11]: Windows-10-10.0.19042-SP0

In[12]: sys.version
Out[12]: '3.8.12 (default, Oct 12 2021, 03:01:40) [MSC v.1916 64 bit (AMD64)]'

In[13]: IPython.__version__
Out[13]: '8.0.1'

In[14]: rpyc.__version__
Out[14]: (5, 0, 1)

How do we replicate the issue?

Changing the variable type attribute to int type or something when returning message from qudi to ipython console, I could avoid this problem for the time being.

In[134]: int(laser_dummy.on())
Out[134]: 1

Expected behavior

In[1]: laser_dummy.on()
Out[1]: <LaserState.ON: 1>

Relevant log output

No response

Additional Comments

No response

Contact Details

No response

Neverhorst commented 2 years ago

Hi @kiotex and thank you for pointing that out. This is actually a known issue and comes from the fact that all references/objects returned from the qudi process into the IPython kernel are actually rpyc references (rpyc.core.netref type). This is necessary because your IPython session (e.g. jupyter notebook or the qudi manager console) is a separate process and is communicating with the qudi process via rpyc. While that circumstance in itself is not so bad, IPython has some very weird (and undocumented) formatting machinery for printing the output cell.

One can currently avoid the isssue by explicitly printing the returned object instead of relying on the IPython output mechanics. So instead of:

In[1]: laser_dummy.on()

This here should work instead:

In[1]: print(laser_dummy.on())

However: I just pushed a workaround to main that should avoid this printing issue by disabling the IPython output formatter entirely. So all outputs cells should be equivalent to a call to print. Could you please check if this resolves the issue for you?

But there are still things that can go wrong when using an interactive IPython session. rpyc, numpy and jupyter/IPython are very incompatible and care needs to be taken when working in a notebook/the qudi console. There is currently no perfect solution to this isssue out there as far as I know.

kiotex commented 2 years ago

Thanks a lot, @Neverhorst

I just pushed a workaround to main that should avoid this printing issue by disabling the IPython output formatter entirely. So all outputs cells should be equivalent to a call to print. Could you please check if this resolves the issue for you?

It seems to work I expect.

Neverhorst commented 2 years ago

You're welcome @kiotex. I will close this issue.

Neverhorst commented 2 years ago

@kiotex Unfortunately, I had to undo this fix again. A side effect of the whole thing was that plotting in jupyter notebooks was not possible anymore. So please use the workaround I posted above with calling print() explicitly. IPython is a mess and I can not find any way to avoid these errors selectively.