Repository: wyfo/apischema

Issue:
Deserialization failure when a deserialization occurs before adding a deserializer
Performing deserializations prevents later addition of deserializers.
For correctness. It does not correspond to a use case, since I encountered this problem while I was debugging.
It is most likely due to the internal caching.

```python
import pytest
from apischema import ValidationError, deserialize, deserializer
from apischema.conversions import Conversion, catch_value_error

class Foo(int):
    pass

deserialize(Foo, 1)   # <--- by removing this line, no ValidationError will be raised below

deserializer(Conversion(catch_value_error(Foo), source=str, target=Foo))
with pytest.raises(ValidationError):
    deserialize(Foo, '1')

```


Discussion:

Patch:
diff --git a/apischema/conversions/converters.py b/apischema/conversions/converters.py
index a724c88..84f959d 100644
--- a/apischema/conversions/converters.py
+++ b/apischema/conversions/converters.py
@@ -7,9 +7,9 @@ from typing import (
     TYPE_CHECKING,
     Any,
     Callable,
-    List,
     MutableMapping,
     Optional,
+    Tuple,
     Type,
     TypeVar,
     Union,
@@ -37,8 +37,8 @@ if TYPE_CHECKING:
     pass
 
 
-_deserializers: MutableMapping[AnyType, List[ConvOrFunc]] = CacheAwareDict(
-    defaultdict(list)
+_deserializers: MutableMapping[AnyType, Tuple[ConvOrFunc, ...]] = CacheAwareDict(
+    defaultdict(tuple)
 )
 _serializers: MutableMapping[AnyType, ConvOrFunc] = CacheAwareDict({})
 Deserializer = TypeVar(
@@ -54,7 +54,7 @@ if sys.version_info < (3, 8):
         return _deserializers.get(tp)
 
 else:
-    default_deserialization = _deserializers.get  # type: ignore
+    default_deserialization = _deserializers.get
 
 
 def default_serialization(tp: Type) -> Optional[AnyConversion]:
@@ -83,7 +83,7 @@ def check_converter_type(tp: AnyType) -> AnyType:
 def _add_deserializer(conversion: ConvOrFunc, target: AnyType):
     target = check_converter_type(target)
     if conversion not in _deserializers[target]:
-        _deserializers[target].append(conversion)
+        _deserializers[target] = *_deserializers[target], conversion
 
 
 class DeserializerDescriptor(MethodWrapper[staticmethod]):


Test patch:
diff --git a/tests/integration/test_deserializer_registration_reset_deserialization_cache.py b/tests/integration/test_deserializer_registration_reset_deserialization_cache.py
new file mode 100644
index 0000000..fb4250e
--- /dev/null
+++ b/tests/integration/test_deserializer_registration_reset_deserialization_cache.py
@@ -0,0 +1,16 @@
+import pytest
+
+from apischema import ValidationError, deserialize, deserializer
+from apischema.conversions import Conversion, catch_value_error
+
+
+class Foo(int):
+    pass
+
+
+def test_deserializer_registration_reset_deserialization_cache():
+    assert deserialize(Foo, 1) == Foo(1)
+    deserializer(Conversion(catch_value_error(Foo), source=str, target=Foo))
+    assert deserialize(Foo, "1") == Foo(1)
+    with pytest.raises(ValidationError):
+        deserialize(Foo, 1)