Repository: datalad/datalad-next

Issue:
`AnnexKey` should be hashable
I would think that using this type as a key in a mapping would be a standard use case. It would require the following change:

```diff
diff --git a/datalad_next/types/annexkey.py b/datalad_next/types/annexkey.py
index 74bbcb6..5a85342 100644
--- a/datalad_next/types/annexkey.py
+++ b/datalad_next/types/annexkey.py
@@ -14,7 +14,7 @@ _annexkey_regex = re.compile(
 )
 
 
-@dataclass
+@dataclass(frozen=True)
 class AnnexKey:
     """Representation of a git-annex key
 
```

This would mean that post-init assignment of properties is not possible. I tend to think that this is OK.

Discussion:

Patch:
diff --git a/datalad_next/types/annexkey.py b/datalad_next/types/annexkey.py
index 74bbcb6..037280b 100644
--- a/datalad_next/types/annexkey.py
+++ b/datalad_next/types/annexkey.py
@@ -1,3 +1,5 @@
+"""git-annex key representation"""
+
 from __future__ import annotations
 
 from dataclasses import dataclass
@@ -14,7 +16,7 @@ _annexkey_regex = re.compile(
 )
 
 
-@dataclass
+@dataclass(frozen=True)
 class AnnexKey:
     """Representation of a git-annex key
 
@@ -29,6 +31,7 @@ class AnnexKey:
 
     @classmethod
     def from_str(cls, key: str):
+        """Return an ``AnnexKey`` instance from a key string"""
         key_matched = _annexkey_regex.match(key)
         if not key_matched:
             # without a sensible key there is no hope


Test patch:
diff --git a/datalad_next/types/tests/test_annexkey.py b/datalad_next/types/tests/test_annexkey.py
index 80c68c0..a4e3878 100644
--- a/datalad_next/types/tests/test_annexkey.py
+++ b/datalad_next/types/tests/test_annexkey.py
@@ -1,9 +1,26 @@
+import pytest
+
 from ..annexkey import AnnexKey
 
 
 def test_annexkey():
     for key in (
         'MD5E-s792207360--985e680a221e47db05063a12b91d7d89.tar',
+        'SHA256E-s31390--f50d7ac4c6b9031379986bc362fcefb65f1e52621ce1708d537e740fefc59cc0.mp3',
+        'URL-s1899248--http&c%%ai.stanford.edu%,126nilsson%MLBOOK.pdf/URL-s1899248--http&c%%ai.stanford.edu%,126nilsson%MLBOOK.pdf',
     ):
         # round-tripping for any key must give same outcome
         assert key == str(AnnexKey.from_str(key))
+
+    # check that it can be used as a dict-key, i.e. is hashable
+    key = AnnexKey.from_str('MD5-s9--985e680a221e47db05063a12b91d7d89')
+    d = {key: 'some'}
+
+
+def test_annexkey_errors():
+    for wrong in (
+        'MD5E-985e680a221e47db05063a12b91d7d89.tar',
+        'MD5E-SUPRISE--985e680a221e47db05063a12b91d7d89.tar',
+    ):
+        with pytest.raises(ValueError):
+            AnnexKey.from_str(wrong)