{"action":"created","issue":{"url":"https://api.github.com/repos/ReactiveX/RxPY/issues/209","repository_url":"https://api.github.com/repos/ReactiveX/RxPY","labels_url":"https://api.github.com/repos/ReactiveX/RxPY/issues/209/labels{/name}","comments_url":"https://api.github.com/repos/ReactiveX/RxPY/issues/209/comments","events_url":"https://api.github.com/repos/ReactiveX/RxPY/issues/209/events","html_url":"https://github.com/ReactiveX/RxPY/issues/209","id":267405492,"number":209,"title":"Rx seems to hide the true origin of a TypeError exception raised in a selector function","user":{"login":"jcafhe","id":11948994,"avatar_url":"https://avatars3.githubusercontent.com/u/11948994?v=4","gravatar_id":"","url":"https://api.github.com/users/jcafhe","html_url":"https://github.com/jcafhe","followers_url":"https://api.github.com/users/jcafhe/followers","following_url":"https://api.github.com/users/jcafhe/following{/other_user}","gists_url":"https://api.github.com/users/jcafhe/gists{/gist_id}","starred_url":"https://api.github.com/users/jcafhe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcafhe/subscriptions","organizations_url":"https://api.github.com/users/jcafhe/orgs","repos_url":"https://api.github.com/users/jcafhe/repos","events_url":"https://api.github.com/users/jcafhe/events{/privacy}","received_events_url":"https://api.github.com/users/jcafhe/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2017-10-21T19:00:33Z","updated_at":"2017-10-26T11:57:19Z","closed_at":null,"author_association":"NONE","body":"Hello,\r\nI'm having issues with how Rx handles TypeError exception raised in a function with Rx 1.6.0, python 3.5.3 and python 3.4.5 . The code below is a minimal example that reproduces the issue:\r\n\r\n```python\r\n# -*- coding: utf-8 -*-\r\nimport rx\r\n\r\n\r\ndef compute(x):\r\n#    raise Exception('Foo')\r\n#    raise ArithmeticError('Foo')\r\n#    raise AttributeError('Foo')\r\n#    raise BufferError('Foo')\r\n#    raise EOFError('Foo')\r\n#    raise OSError('Foo')\r\n#    raise RuntimeError('Foo')\r\n#    raise SystemError('Foo')\r\n    raise TypeError('Foo') # this one is not working correctly\r\n#    raise ValueError('Foo')\r\n    return x\r\n\r\n\r\n(rx.Observable\r\n   .just(1)\r\n   .map(lambda x: compute(x))\r\n   .subscribe(print)\r\n )\r\n```\r\n\r\nThe *compute(x)* function raises a specific Exception (comment/uncomment lines). When raising TypeError, I get a TypeError printed on console, but the traceback is weird :\r\n\r\n```python\r\nTraceback (most recent call last):\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\runpy.py\", line 183, in _run_module_as_main\r\n    mod_name, mod_spec, code = _get_module_details(mod_name, _Error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\runpy.py\", line 109, in _get_module_details\r\n    __import__(pkg_name)\r\n  File \"C:\\DEV\\pyProjects\\tp_QT\\roaming\\issues\\rx_catch_exception_simplified.py\", line 22, in <module>\r\n    .subscribe(print)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observablebase.py\", line 83, in subscribe\r\n    current_thread_scheduler.schedule(set_disposable)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\currentthreadscheduler.py\", line 50, in schedule\r\n    return self.schedule_relative(timedelta(0), action, state)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\currentthreadscheduler.py\", line 67, in schedule_relative\r\n    Trampoline.run(queue)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\currentthreadscheduler.py\", line 31, in run\r\n    item.invoke()\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\scheduleditem.py\", line 17, in invoke\r\n    ret = self.scheduler.invoke_action(self.action, self.state)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\schedulerbase.py\", line 14, in invoke_action\r\n    ret = action(self, state)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\linq\\observable\\returnvalue.py\", line 30, in action\r\n    observer.on_next(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 19, in on_next\r\n    self._on_next_core(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\autodetachobserver.py\", line 16, in _on_next_core\r\n    self.observer.on_next(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 19, in on_next\r\n    self._on_next_core(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\anonymousobserver.py\", line 14, in _on_next_core\r\n    self._next(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\linq\\observable\\select.py\", line 33, in on_next\r\n    observer.on_error(err)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 33, in on_error\r\n    self._on_error_core(error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\autodetachobserver.py\", line 23, in _on_error_core\r\n    self.observer.on_error(exn)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 33, in on_error\r\n    self._on_error_core(error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\anonymousobserver.py\", line 17, in _on_error_core\r\n    self._error(error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\internal\\basic.py\", line 33, in default_error\r\n    raise err\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\linq\\observable\\select.py\", line 31, in on_next\r\n    result = selector(value, count[0])\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\internal\\utils.py\", line 42, in func_wrapped\r\n    return func(*args, **kw)\r\nTypeError: <lambda>() takes 1 positional argument but 2 were given \r\n```\r\n\r\nSo here we've got a TypeError but the trace is not pointing to the rigth place. In fact, this is a different exception with the same etype. If I raise another exception, let's say an ArithmeticError, i get the expected behaviour with a traceback pointing to the right line:\r\n\r\n```python\r\nTraceback (most recent call last):\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\runpy.py\", line 183, in _run_module_as_main\r\n    mod_name, mod_spec, code = _get_module_details(mod_name, _Error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\runpy.py\", line 109, in _get_module_details\r\n    __import__(pkg_name)\r\n  File \"C:\\DEV\\pyProjects\\tp_QT\\roaming\\issues\\rx_catch_exception_simplified.py\", line 22, in <module>\r\n    .subscribe(print)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observablebase.py\", line 83, in subscribe\r\n    current_thread_scheduler.schedule(set_disposable)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\currentthreadscheduler.py\", line 50, in schedule\r\n    return self.schedule_relative(timedelta(0), action, state)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\currentthreadscheduler.py\", line 67, in schedule_relative\r\n    Trampoline.run(queue)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\currentthreadscheduler.py\", line 31, in run\r\n    item.invoke()\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\scheduleditem.py\", line 17, in invoke\r\n    ret = self.scheduler.invoke_action(self.action, self.state)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\concurrency\\schedulerbase.py\", line 14, in invoke_action\r\n    ret = action(self, state)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\linq\\observable\\returnvalue.py\", line 30, in action\r\n    observer.on_next(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 19, in on_next\r\n    self._on_next_core(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\autodetachobserver.py\", line 16, in _on_next_core\r\n    self.observer.on_next(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 19, in on_next\r\n    self._on_next_core(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\anonymousobserver.py\", line 14, in _on_next_core\r\n    self._next(value)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\linq\\observable\\select.py\", line 33, in on_next\r\n    observer.on_error(err)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 33, in on_error\r\n    self._on_error_core(error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\autodetachobserver.py\", line 23, in _on_error_core\r\n    self.observer.on_error(exn)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\observerbase.py\", line 33, in on_error\r\n    self._on_error_core(error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\core\\anonymousobserver.py\", line 17, in _on_error_core\r\n    self._error(error)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\internal\\basic.py\", line 33, in default_error\r\n    raise err\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\linq\\observable\\select.py\", line 31, in on_next\r\n    result = selector(value, count[0])\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\internal\\utils.py\", line 33, in func_wrapped\r\n    ret = fn(*args, **kw)\r\n  File \"C:\\DEV\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\internal\\utils.py\", line 22, in func1\r\n    return func(arg1)\r\n  File \"C:\\DEV\\pyProjects\\tp_QT\\roaming\\issues\\rx_catch_exception_simplified.py\", line 21, in <lambda>\r\n    .map(lambda x: compute(x))\r\n  File \"C:\\DEV\\pyProjects\\tp_QT\\roaming\\issues\\rx_catch_exception_simplified.py\", line 7, in compute\r\n    raise ArithmeticError('Foo')\r\nArithmeticError: Foo\r\n```\r\nI have tried with all type of exception commented in the compute function. All of these work as expected except for TypeError.\r\n\r\nI've looked at [line 42 in rx/internal/utils.py](https://github.com/ReactiveX/RxPY/blob/63c7e4bd6cc394365d2068dfbe0ae60401beabfc/rx/internal/utils.py#L42) and I think there is an issue with the adapt_call() function. From what I understand, adapt_call() is used to 'reduce' the number of arguments that will be provided to the selector function using a try-except-TypeError check to detect how many parameters are accepted. So, there should be a conflict between a TypeError raised because of two much arguments and a TypeError raised inside the selector. With the example above :\r\n* On the first try-except-TypeError evaluation with func1 (only 1 parameter), the selector function *lambda x: compute(x)* raises inside its body a TypeError which leads to confusion because selector has only one parameter and Rx supposes it has more than one. \r\n* On the 2nd try-except-TypeError  with func2 (2 parameters), a TypeError is raised since selector has only one parameter.\r\n* finally, at line 42, selector function is feeded with too much arguments (two) since the first check was missed which leads to a 'TypeError: <lambda>() lambda takes 1 positional argument but 2 were given'.  I've checked with pdb with the example above and it confirms that args contains 2 values.\r\n\r\n```python\r\nTypeError: <lambda>() takes 1 positional argument but 2 were given\r\nUncaught exception. Entering post mortem debugging\r\nRunning 'cont' or 'step' will restart the program\r\n> c:\\dev\\anaconda3_4.2.0\\envs\\bigboom\\lib\\site-packages\\rx\\internal\\utils.py(42)func_wrapped()\r\n-> return func(*args, **kw)\r\n(Pdb) args\r\nargs = (1, 0)\r\nkw = {}\r\n(Pdb) kw\r\n{}\r\n```\r\n\r\nAfter looking at rx/internals/utils.py history, it appears that before simplification done on commit 63c7e4bd6cc394365d2068dfbe0ae60401beabfc,  there were 2 versions of *adapt_call(selector)* function. One of these did not rely on try-exept-TypeError and used *inspect.getargspec(func)* instead. \r\n\r\nI would suggest to use, for python 3, the *inspect.signature(func)* and *inspect.Signature.bind(\\*args, \\*\\*kwargs)* since *inspect.getargspec(func)* is deprecated. \r\n\r\nAccording to the [documentation](https://docs.python.org/3.5/library/inspect.html#inspect.signature), *inspect.signature(func)* return a Signature object from a function.  Then, *Signature.bind(\\*args, \\*\\*kwargs)* raises a TypeError if args and kwargs don't match the signature.\r\n\r\n~~Maybe all of this is related to 51.~~\r\n"},"comment":{"url":"https://api.github.com/repos/ReactiveX/RxPY/issues/comments/339642447","html_url":"https://github.com/ReactiveX/RxPY/issues/209#issuecomment-339642447","issue_url":"https://api.github.com/repos/ReactiveX/RxPY/issues/209","id":339642447,"user":{"login":"jcafhe","id":11948994,"avatar_url":"https://avatars3.githubusercontent.com/u/11948994?v=4","gravatar_id":"","url":"https://api.github.com/users/jcafhe","html_url":"https://github.com/jcafhe","followers_url":"https://api.github.com/users/jcafhe/followers","following_url":"https://api.github.com/users/jcafhe/following{/other_user}","gists_url":"https://api.github.com/users/jcafhe/gists{/gist_id}","starred_url":"https://api.github.com/users/jcafhe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcafhe/subscriptions","organizations_url":"https://api.github.com/users/jcafhe/orgs","repos_url":"https://api.github.com/users/jcafhe/repos","events_url":"https://api.github.com/users/jcafhe/events{/privacy}","received_events_url":"https://api.github.com/users/jcafhe/received_events","type":"User","site_admin":false},"created_at":"2017-10-26T11:57:19Z","updated_at":"2017-10-26T11:57:19Z","author_association":"NONE","body":"Yes it does ! I have also tried with more complex pipelines than the minimal example and it works for me.\r\n\r\nSorry for the delay, I am a git/github newbie. Should I close this issue now or should I wait the next release ?"}}
{"actor":{"display_login":"jcafhe"}}