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

pytest.raises 3.5.0 breaks for exception classes that look iterable (e.g. spyne.error.RequestNotAllowed) #3372

Closed
backbord opened this Issue Apr 6, 2018 · 5 comments

Comments

Projects
None yet
4 participants
@backbord

backbord commented Apr 6, 2018

Hi,

I recently update to pytest 3.5.0 and use it to test applications that use spyne.

Unfortunately, my tests that have code like

import pytest
from spyne.errors import RequestNotAllowed, InvalidCredentialsError

import mymodule

def test_a():
    with pytest.raises(RequestNotAllowed):
        mymodule.func_that_raises_request_not_allowed()

def test_b():
    with pytest.raises(InvalidCredentialsError):
        mymodule.func_that_raises_invalid_credentials_error()

now break in the lines starting on with pytest.raises and give a traceback similar to this one:

___ test_detect_replay_nonce[example_forecast.xml.gz] ___

    def test_a():
>       with pytest.raises(RequestNotAllowed):

tests/test_client.py:130: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/foobar/lib/python3.6/site-packages/_pytest/python_api.py:587: in raises
    for exc in filterfalse(isclass, always_iterable(expected_exception)):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
self = <class 'spyne.error.RequestNotAllowed'>, item = 0

    def __getitem__(self, item):
>       return self.customize(**item)
E       TypeError: customize() argument after ** must be a mapping, not int

/foobar/lib/python3.6/site-packages/spyne/model/_base.py:179: TypeError

The issue didn't come up in previous versions of pytest so I dug a bit and found that

  • pytest.raises changed in version 3.5.0 to use always_iterable,
  • the exception classes used by spyne both have a __len__ and a __getitem__ function,
  • python thinks that the exception class is iterable,
    and that this is the cause of these unexpected problems.

I don't know whether exception classes that look iterable are generally disallowed or if this is something that should be supported/fixed in pytest.
I didn't find this behavior change to be mentioned in the changelog but I may have not looked thoroughly enough.

Perhaps you can advise?

Thanks!
Tim


Here is some IPython output from my digging:

Python 3.6.5
Type 'copyright', 'credits' or 'license' for more information
IPython 6.3.0 -- An enhanced Interactive Python. Type '?' for help.
Warning: disable autoreload in ipython_config.py to improve performance.

In [1]: from spyne.error import InvalidCredentialsError

In [2]: hasattr(InvalidCredentialsError, '__getitem__')
Out[2]: True

In [3]: hasattr(InvalidCredentialsError, '__len__')
Out[3]: True

In [4]: from more_itertools.more import always_iterable

In [5]: next(always_iterable(InvalidCredentialsError))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-24d61822a93e> in <module>()
----> 1 next(always_iterable(InvalidCredentialsError))

/foobar/lib/python3.6/site-packages/spyne/model/_base.py in __getitem__(self, item)
    177 class ModelBaseMeta(type(object)):
    178     def __getitem__(self, item):
--> 179         return self.customize(**item)
    180
    181     def customize(self, **kwargs):

TypeError: customize() argument after ** must be a mapping, not int

always_iterable attempts to create an iterable by executing

try:
    return iter(obj)
except TypeError:
    return iter((obj,))
In [6]: next(iter(InvalidCredentialsError))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-5759e2f7cde0> in <module>()
----> 1 next(iter(InvalidCredentialsError))

/foobar/lib/python3.6/site-packages/spyne/model/_base.py in __getitem__(self, item)
    177 class ModelBaseMeta(type(object)):
    178     def __getitem__(self, item):
--> 179         return self.customize(**item)
    180
    181     def customize(self, **kwargs):

TypeError: customize() argument after ** must be a mapping, not int

I'm using

  • python 3.6.5
  • pytest 3.5.0
  • spyne 2.12.14

It works in pytest 3.4.2 where always_iterable isn't used in functionraises of _pytest/python_api.py.

@pytestbot

This comment has been minimized.

pytestbot commented Apr 6, 2018

GitMate.io thinks possibly related issues are #1965 (Pytest 3.0.2 memory leak with pytest.raises), #2573 (3.2.0: exceptions / internal errors related to markers), #2118 (pytest triggering deprecation warnings in 3.0.5), #1882 (pytest 3.0 on Anaconda), and #1005 (Error in Python 3.5.0).

@RonnyPfannschmidt

This comment has been minimized.

Member

RonnyPfannschmidt commented Apr 6, 2018

at first glance - wow they just implemented a fundamentally broken sequence as part of an exception api for convenience - the code change in queston is a internal clenaup

@RonnyPfannschmidt

This comment has been minimized.

Member

RonnyPfannschmidt commented Apr 6, 2018

@nicoddemus i believe a reasonable fix is to set the base_type parameter to Exception in our call,

alltho it may be necessary to use (Exception, six.string_types)

@nicoddemus

This comment has been minimized.

Member

nicoddemus commented Apr 6, 2018

at first glance - wow they just implemented a fundamentally broken sequence as part of an exception api for convenience

😱

@nicoddemus i believe a reasonable fix is to set the base_type parameter to Exception in our call,

If that works then it sounds good!

@backbord are you interested in opening a PR?

nicoddemus added a commit that referenced this issue Apr 7, 2018

@nicoddemus nicoddemus closed this Apr 7, 2018

@nicoddemus

This comment has been minimized.

Member

nicoddemus commented Apr 7, 2018

Fixed by #3373

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment