Skip to content

Collection error when HTTPError passed to parametrize #10184

@jaraco

Description

@jaraco

Using the latest version of pytest against this minimal reproducer produces an error in collection:

 draft $ cat test.py
from urllib.request import HTTPError

import pytest


@pytest.mark.parametrize(
    'error', [HTTPError('url', 400, 'httperror', {}, None)])
def test_something(error):
    pass
 draft $ pip-run pytest -- -m pytest test.py
Collecting pytest
  Using cached pytest-7.1.2-py3-none-any.whl (297 kB)
Collecting pluggy<2.0,>=0.12
  Using cached pluggy-1.0.0-py2.py3-none-any.whl (13 kB)
Collecting py>=1.8.2
  Using cached py-1.11.0-py2.py3-none-any.whl (98 kB)
Collecting tomli>=1.0.0
  Using cached tomli-2.0.1-py3-none-any.whl (12 kB)
Collecting iniconfig
  Using cached iniconfig-1.1.1-py2.py3-none-any.whl (5.0 kB)
Collecting attrs>=19.2.0
  Using cached attrs-22.1.0-py2.py3-none-any.whl (58 kB)
Collecting packaging
  Using cached packaging-21.3-py3-none-any.whl (40 kB)
Collecting pyparsing!=3.0.5,>=2.0.2
  Using cached pyparsing-3.0.9-py3-none-any.whl (98 kB)
Installing collected packages: iniconfig, tomli, pyparsing, py, pluggy, attrs, packaging, pytest
Successfully installed attrs-22.1.0 iniconfig-1.1.1 packaging-21.3 pluggy-1.0.0 py-1.11.0 pyparsing-3.0.9 pytest-7.1.2 tomli-2.0.1
=============================================================================== test session starts ===============================================================================
platform darwin -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0
rootdir: /Users/jaraco/draft
collected 0 items / 1 error                                                                                                                                                       

===================================================================================== ERRORS ======================================================================================
____________________________________________________________________________ ERROR collecting test.py _____________________________________________________________________________
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/pluggy/_hooks.py:265: in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/pluggy/_manager.py:80: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:262: in pytest_pycollect_makeitem
    return list(collector._genfunctions(name, obj))
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:489: in _genfunctions
    self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/pluggy/_hooks.py:292: in call_extra
    return self(**kwargs)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/pluggy/_hooks.py:265: in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/pluggy/_manager.py:80: in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:148: in pytest_generate_tests
    metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:1304: in parametrize
    ids = self._resolve_parameter_set_ids(
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:1374: in _resolve_parameter_set_ids
    return id_maker.make_unique_parameterset_ids()
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:966: in make_unique_parameterset_ids
    resolved_ids = list(self._resolve_ids())
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:991: in _resolve_ids
    yield "-".join(
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:992: in <genexpr>
    self._idval(val, argname, idx)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:1004: in _idval
    idval = self._idval_from_value(val)
/var/folders/sx/n5gkrgfx6zd91ymxr2sr9wvw00n8zm/T/pip-run-7rkih960/_pytest/python.py:1051: in _idval_from_value
    elif isinstance(getattr(val, "__name__", None), str):
/opt/homebrew/Cellar/python@3.10/3.10.5/Frameworks/Python.framework/Versions/3.10/lib/python3.10/tempfile.py:477: in __getattr__
    file = self.__dict__['file']
E   KeyError: 'file'
============================================================================= short test summary info =============================================================================
ERROR test.py - KeyError: 'file'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
================================================================================ 1 error in 0.11s =================================================================================

The same error can be elicited by trying to get the __name__ property of an HTTPError:

>>> import urllib.request
>>> urllib.request.HTTPError().__name__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() missing 5 required positional arguments: 'url', 'code', 'msg', 'hdrs', and 'fp'
>>> urllib.request.HTTPError('url', 400, 'httperror', {}, None).__name__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/Cellar/python@3.9/3.9.13_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tempfile.py", line 472, in __getattr__
    file = self.__dict__['file']
KeyError: 'file'

I'm not sure if there's anything that can be done about this situation. In the code where I encountered it, I've decided to pass the parameter as a string and then eval it inside the test, a workaround to be sure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions