jcrist / msgspec

A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML
https://jcristharif.com/msgspec/
BSD 3-Clause "New" or "Revised" License
2.44k stars 75 forks source link

Struct import fails in pytest - Missing requirement argument 'attribute x' or unhashable type: 'StructClass' #660

Closed neotechmonk closed 1 month ago

neotechmonk commented 8 months ago

Description

Setup Struct based class

from typing import Annotated
import msgspec

class TestModel(msgspec.Struct):
    key: Annotated[str, msgspec.Meta(max_length=6)] 

Importing the TestModel for further unit testing

from tests.model import TestModel

When I run pytest on the unittest module I get this error

.venv/lib/python3.11/site-packages/_pytest/runner.py:340: in from_call
    result: Optional[TResult] = func()
.venv/lib/python3.11/site-packages/_pytest/runner.py:388: in collect
    return list(collector.collect())
.venv/lib/python3.11/site-packages/_pytest/python.py:794: in collect
    self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid)
.venv/lib/python3.11/site-packages/_pytest/python.py:767: in newinstance
    return self.obj()
E   TypeError: Missing required argument 'key'

The above error is addressed by assigning a default value key: Annotated[str, msgspec.Meta(max_length=6)] = "test"

However running pytest again leads to this error

.venv/lib/python3.11/site-packages/_pytest/runner.py:340: in from_call
    result: Optional[TResult] = func()
.venv/lib/python3.11/site-packages/_pytest/runner.py:388: in collect
    return list(collector.collect())
.venv/lib/python3.11/site-packages/_pytest/python.py:794: in collect
    self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid)
.venv/lib/python3.11/site-packages/_pytest/fixtures.py:1713: in parsefactories
    if holderobj in self._holderobjseen:
E   TypeError: unhashable type: 'TestModel

When I implement __hash__() like the below both errors are resolved

class TestModel(msgspec.Struct):
    key: Annotated[str, msgspec.Meta(max_length=6)] = "test"

    def __hash__(self):
        return hash((self.key,))

Is this is an issue or am I using the library in a wrong way?

jcrist commented 1 month ago

Apologies for the delayed response here. This is not a bug in msgspec but rather a pytest behavior. When pytest collects tests it assumes any class named Test* is a test class and tries to instantiate it and collect tests off it. If you rename your struct type to something that doesn't start with "Test" then things should work fine.