Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Cryptic Error Message when module-level __getattr__ fails to raise AttributeError #8265

@Erotemic

Description

@Erotemic

I just ran into an error that was very difficult to figure out. This is not a bug in pytest perse, but it is an area that might benefit from having a better error message.

In Python 3.7+ you are now allowed to define a custom __getattr__ at the module level. I find this incredibly useful giving users deprecation errors when they try to use something that was recently moved or removed. However, I made a mistake in my last one, and instead of raising an AttributeError in the fallback case, I forgot and let the default None return.

This actually doesn't hurt anything in normal usage, because only attributes that exist are ever accessed. However, it seems like pytest attempts to do a getattr at the module level in normalize_mark_list defined in _pytest.mark.structures. Thus if I define the file:

#pytest_getattr_mwe.py


def __getattr__(key):
    return None
    # raise AttributeError(key)


def test_1():
    return 1

and run pytest pytest_getattr_mwe.py I get:

(py38) joncrall@Ooo:~/misc/tests/python$ pytest pytest_getattr_mwe.py 
=============================================================================== test session starts ================================================================================
platform linux -- Python 3.8.2, pytest-6.2.1, py-1.9.0, pluggy-0.13.1
rootdir: /home/joncrall/misc/tests/python
plugins: celery-4.4.7, timeout-1.4.1, notebook-0.6.0, hydra-core-1.0.5, lazy-fixture-0.6.3, cov-2.8.1, xdoctest-0.15.2
collected 0 items / 1 error                                                                                                                                                        

====================================================================================== ERRORS ======================================================================================
______________________________________________________________________ ERROR collecting pytest_getattr_mwe.py ______________________________________________________________________
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/python.py:503: in collect
    self._inject_setup_module_fixture()
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/python.py:516: in _inject_setup_module_fixture
    self.obj, ("setUpModule", "setup_module")
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/python.py:295: in obj
    self.own_markers.extend(get_unpacked_marks(self.obj))
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/mark/structures.py:353: in get_unpacked_marks
    return normalize_mark_list(mark_list)
../../../.local/conda/envs/py38/lib/python3.8/site-packages/_pytest/mark/structures.py:367: in normalize_mark_list
    raise TypeError(f"got {mark!r} instead of Mark")
E   TypeError: got None instead of Mark

Perhaps it would be possible to improve this error message? For instance if mark is None maybe hint that __getattr__ may have been ill-defined? (I'm not sure if this can happen in other cases).

The thing that made this so hard for me is that I have no idea what a pytest mark does. I try not to use any of its magic or decorator features in favor of more explicit code. Thus when I got an error involving whatever a mark, I was quite confused.

At the very least, I hope posting this will help some poor soul who googles the error message "TypeError: got None instead of Mark", and instead of getting nothing like I did, maybe this will come up and then they will realize they forgot to raise AttributeError at the end of their __getattr__.

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: marksrelated to marks, either the general marks or builtintype: bugproblem that needs to be addressed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions