1st1: > Oh right, so should we make the pure Python version also implement __class_get_item__ instead?

Yes, that's the idea.

> Because I believe Mapping.register(Map) is not sufficient for making Map[foo, bar] return Map?

True.  We only need the `Mapping.register()` part to make `isinstance(immumap, Mapping)` work.
antonagestam: @1st1 Gotcha, thanks for taking the time to explain! I'll try to update the PR soon :) 
antonagestam: I added fixes that addresses your comments as well as a test case. Perhaps the documentation should be updated with an example as well? 
1st1: Looks great! Only a few nits left.
antonagestam: I have a few questions on the mutation API.

Should Map.mutate() always return a MapMutation with it's own signature? E.g. `Map[A, B]().mutate() => MapMutation[A, B]`? I think that would make sense as then MapMutation.finish() would return the same type Map.

As for both MapMutation and Map should I add type hints for all methods or just the ones that are part of the public API? There's a few methods that always raise an exception, like MapMutation.__iter__(), should these have the return type `typing.NoReturn` or just not be hinted?

I'm also curious as to why MapMutation.pop() accepts a variadic *args and then raises TypeError if it's longer than 1. Why not use a non-variadic single `default` argument? :)
1st1: > Should Map.mutate() always return a MapMutation with it's own signature? E.g. Map[A, B]().mutate() => MapMutation[A, B]?

Yes.

> As for both MapMutation and Map should I add type hints for all methods or just the ones that are part of the public API? 

Ideally, yes.

> There's a few methods that always raise an exception, like MapMutation.__iter__(), should these have the return type typing.NoReturn or just not be hinted?

I guess using `typing.NoReturn` makes sense.

> I'm also curious as to why MapMutation.pop() accepts a variadic *args and then raises TypeError if it's longer than 1. Why not use a non-variadic single default argument? :)

This is to emulate a "positional-only argument", IIRC. 
antonagestam: I added more type hints, so the mutation API should now be covered as well as the keys, values and items methods. I added a `BitmapNode` as an empty class because it is exposed as the type of the `m` arguments in `__init__` methods. However, it's implementation was not to decipher and so I left it untyped for now. I don't think I'll be able to figure out the correct type hints for it.
1st1: @ambv
antonagestam: Ping me if you need me to rebase or make other changes :)
diff --git a/immutables/_map.c b/immutables/_map.c
index f9c8772..7e63562 100644
--- a/immutables/_map.c
+++ b/immutables/_map.c
@@ -3363,6 +3363,12 @@ map_reduce(MapObject *self)
     return tup;
 }
 
+static PyObject *
+map_py_class_getitem(PyObject *type, PyObject *item)
+{
+    Py_INCREF(type);
+    return type;
+}
 
 static PyMethodDef Map_methods[] = {
     {"set", (PyCFunction)map_py_set, METH_VARARGS, NULL},
@@ -3375,6 +3381,12 @@ static PyMethodDef Map_methods[] = {
     {"update", (PyCFunction)map_py_update, METH_VARARGS | METH_KEYWORDS, NULL},
     {"__reduce__", (PyCFunction)map_reduce, METH_NOARGS, NULL},
     {"__dump__", (PyCFunction)map_py_dump, METH_NOARGS, NULL},
+    {
+        "__class_getitem__",
+        (PyCFunction)map_py_class_getitem,
+        METH_O|METH_CLASS,
+        NULL
+    },
     {NULL, NULL}
 };
 
diff --git a/immutables/_map.pyi b/immutables/_map.pyi
new file mode 100644
index 0000000..863d911
--- /dev/null
+++ b/immutables/_map.pyi
@@ -0,0 +1,87 @@
+from typing import Any
+from typing import Generic
+from typing import Hashable
+from typing import Iterable
+from typing import Iterator
+from typing import Literal
+from typing import Mapping
+from typing import MutableMapping
+from typing import NoReturn
+from typing import Tuple
+from typing import Type
+from typing import TypeVar
+from typing import Union
+
+
+K = TypeVar('K', bound=Hashable)
+V = TypeVar('V', bound=Any)
+D = TypeVar('D', bound=Any)
+
+
+class BitmapNode: ...
+
+
+class MapKeys(Generic[K]):
+    def __init__(self, c: int, m: BitmapNode) -> None: ...
+    def __len__(self) -> int: ...
+    def __iter__(self) -> Iterator[K]: ...
+
+
+class MapValues(Generic[V]):
+    def __init__(self, c: int, m: BitmapNode) -> None: ...
+    def __len__(self) -> int: ...
+    def __iter__(self) -> Iterator[V]: ...
+
+
+class MapItems(Generic[K, V]):
+    def __init__(self, c: int, m: BitmapNode) -> None: ...
+    def __len__(self) -> int: ...
+    def __iter__(self) -> Iterator[Tuple[K, V]]: ...
+
+
+class Map(Mapping[K, V]):
+    def __init__(
+        self, col: Union[Mapping[K, V], Iterable[Tuple[K, V]]] = ..., **kw: V
+    ): ...
+    def __reduce__(self) -> NoReturn: ...
+    def __len__(self) -> int: ...
+    def __eq__(self, other: Any) -> bool: ...
+    def update(
+        self, col: Union[Mapping[K, V], Iterable[Tuple[K, V]]] = ..., **kw: V
+    ) -> Map[K, V]: ...
+    def mutate(self) -> MapMutation[K, V]: ...
+    def set(self, key: K, val: V) -> Map[K, V]: ...
+    def delete(self, key: K) -> Map[K, V]: ...
+    def get(self, key: K, default: D = ...) -> Union[V, D]: ...
+    def __getitem__(self, key: K) -> V: ...
+    def __contains__(self, key: object) -> bool: ...
+    def __iter__(self) -> Iterator[K]: ...
+    def keys(self) -> MapKeys[K]: ...
+    def values(self) -> MapValues[V]: ...
+    def items(self) -> MapItems[K, V]: ...
+    def __hash__(self) -> int: ...
+    def __dump__(self) -> str: ...
+    def __class_getitem__(cls, item: Any) -> Type[Map]: ...
+
+
+S = TypeVar('S', bound='MapMutation')
+
+
+class MapMutation(MutableMapping[K, V]):
+    def __init__(self, count: int, root: BitmapNode) -> None: ...
+    def set(self, key: K, val: V) -> None: ...
+    def __enter__(self: S) -> S: ...
+    def __exit__(self, *exc: Any) -> Literal[False]: ...
+    def __iter__(self) -> NoReturn: ...
+    def __delitem__(self, key: K) -> None: ...
+    def __setitem__(self, key: K, val: V) -> None: ...
+    def pop(self, __key: K, __default: D = ...) -> Union[V, D]: ...
+    def get(self, key: K, default: D = ...) -> Union[V, D]: ...
+    def __getitem__(self, key: K) -> V: ...
+    def __contains__(self, key: Any) -> bool: ...
+    def update(
+        self, col: Union[Mapping[K, V], Iterable[Tuple[K, V]]] = ..., **kw: V
+    ): ...
+    def finish(self) -> Map[K, V]: ...
+    def __len__(self) -> int: ...
+    def __eq__(self, other: Any) -> bool: ...
diff --git a/immutables/map.py b/immutables/map.py
index 4c5cd49..3ea4656 100644
--- a/immutables/map.py
+++ b/immutables/map.py
@@ -630,6 +630,9 @@ class Map:
         self.__root.dump(buf, 0)
         return '\n'.join(buf)
 
+    def __class_getitem__(cls, item):
+        return cls
+
 
 class MapMutation:
 
diff --git a/immutables/py.typed b/immutables/py.typed
new file mode 100644
index 0000000..b648ac9
--- /dev/null
+++ b/immutables/py.typed
@@ -0,0 +1,1 @@
+partial

mypy integration
Collections should implement [mypy Mapping generic](https://mypy.readthedocs.io/en/stable/generics.html#defining-sub-classes-of-generic-classes) to be properly typed
diff --git a/tests/test_map.py b/tests/test_map.py
index a99b856..8d629f8 100644
--- a/tests/test_map.py
+++ b/tests/test_map.py
@@ -2,6 +2,7 @@ import collections.abc
 import gc
 import pickle
 import random
+import sys
 import unittest
 import weakref
 
@@ -1296,6 +1297,10 @@ class BaseMapTest:
         with self.assertRaisesRegex(TypeError, "can't pickle"):
             pickle.dumps(h.mutate())
 
+    @unittest.skipIf(sys.version_info < (3, 7, 0), "__class_getitem__ is not available")
+    def test_map_is_subscriptable(self):
+        self.assertIs(self.Map[int, str], self.Map)
+
 
 class PyMapTest(BaseMapTest, unittest.TestCase):
 

pip install -e .
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
exceptiongroup==1.2.2
-e git+https://github.com/MagicStack/immutables.git@1a51a34154654285e017e6006d16c03cbee92637#egg=immutables
importlib-metadata==6.7.0
iniconfig==2.0.0
packaging==24.0
pluggy==1.2.0
pytest==7.4.4
tomli==2.0.1
typing_extensions==4.7.1
zipp==3.15.0

name: immutables
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - packaging==24.0
      - pluggy==1.2.0
      - pytest==7.4.4
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - zipp==3.15.0
prefix: /opt/conda/envs/immutables

tests/test_map.py::PyMapTest::test_map_is_subscriptable
tests/test_map.py::CMapTest::test_map_is_subscriptable
tests/test_map.py::PyMapTest::test_abc_1 tests/test_map.py::PyMapTest::test_hash_1 tests/test_map.py::PyMapTest::test_hash_2 tests/test_map.py::PyMapTest::test_hashkey_helper_1 tests/test_map.py::PyMapTest::test_map_basics_1 tests/test_map.py::PyMapTest::test_map_basics_2 tests/test_map.py::PyMapTest::test_map_basics_3 tests/test_map.py::PyMapTest::test_map_basics_4 tests/test_map.py::PyMapTest::test_map_collision_1 tests/test_map.py::PyMapTest::test_map_collision_2 tests/test_map.py::PyMapTest::test_map_delete_1 tests/test_map.py::PyMapTest::test_map_delete_2 tests/test_map.py::PyMapTest::test_map_delete_3 tests/test_map.py::PyMapTest::test_map_delete_4 tests/test_map.py::PyMapTest::test_map_delete_5 tests/test_map.py::PyMapTest::test_map_delete_6 tests/test_map.py::PyMapTest::test_map_eq_1 tests/test_map.py::PyMapTest::test_map_eq_2 tests/test_map.py::PyMapTest::test_map_eq_3 tests/test_map.py::PyMapTest::test_map_gc_1 tests/test_map.py::PyMapTest::test_map_gc_2 tests/test_map.py::PyMapTest::test_map_getitem_1 tests/test_map.py::PyMapTest::test_map_in_1 tests/test_map.py::PyMapTest::test_map_items_1 tests/test_map.py::PyMapTest::test_map_items_2 tests/test_map.py::PyMapTest::test_map_items_3 tests/test_map.py::PyMapTest::test_map_items_4 tests/test_map.py::PyMapTest::test_map_keys_1 tests/test_map.py::PyMapTest::test_map_keys_2 tests/test_map.py::PyMapTest::test_map_mut_1 tests/test_map.py::PyMapTest::test_map_mut_10 tests/test_map.py::PyMapTest::test_map_mut_11 tests/test_map.py::PyMapTest::test_map_mut_12 tests/test_map.py::PyMapTest::test_map_mut_13 tests/test_map.py::PyMapTest::test_map_mut_14 tests/test_map.py::PyMapTest::test_map_mut_15 tests/test_map.py::PyMapTest::test_map_mut_16 tests/test_map.py::PyMapTest::test_map_mut_17 tests/test_map.py::PyMapTest::test_map_mut_18 tests/test_map.py::PyMapTest::test_map_mut_19 tests/test_map.py::PyMapTest::test_map_mut_2 tests/test_map.py::PyMapTest::test_map_mut_3 tests/test_map.py::PyMapTest::test_map_mut_4 tests/test_map.py::PyMapTest::test_map_mut_5 tests/test_map.py::PyMapTest::test_map_mut_6 tests/test_map.py::PyMapTest::test_map_mut_7 tests/test_map.py::PyMapTest::test_map_mut_8 tests/test_map.py::PyMapTest::test_map_mut_9 tests/test_map.py::PyMapTest::test_map_mut_stress tests/test_map.py::PyMapTest::test_map_pickle tests/test_map.py::PyMapTest::test_map_stress tests/test_map.py::PyMapTest::test_map_values_1 tests/test_map.py::PyMapTest::test_map_values_2 tests/test_map.py::PyMapTest::test_repr_1 tests/test_map.py::PyMapTest::test_repr_2 tests/test_map.py::PyMapTest::test_repr_3 tests/test_map.py::CMapTest::test_abc_1 tests/test_map.py::CMapTest::test_hash_1 tests/test_map.py::CMapTest::test_hash_2 tests/test_map.py::CMapTest::test_hashkey_helper_1 tests/test_map.py::CMapTest::test_map_basics_1 tests/test_map.py::CMapTest::test_map_basics_2 tests/test_map.py::CMapTest::test_map_basics_3 tests/test_map.py::CMapTest::test_map_basics_4 tests/test_map.py::CMapTest::test_map_collision_1 tests/test_map.py::CMapTest::test_map_collision_2 tests/test_map.py::CMapTest::test_map_delete_1 tests/test_map.py::CMapTest::test_map_delete_2 tests/test_map.py::CMapTest::test_map_delete_3 tests/test_map.py::CMapTest::test_map_delete_4 tests/test_map.py::CMapTest::test_map_delete_5 tests/test_map.py::CMapTest::test_map_delete_6 tests/test_map.py::CMapTest::test_map_eq_1 tests/test_map.py::CMapTest::test_map_eq_2 tests/test_map.py::CMapTest::test_map_eq_3 tests/test_map.py::CMapTest::test_map_gc_1 tests/test_map.py::CMapTest::test_map_gc_2 tests/test_map.py::CMapTest::test_map_getitem_1 tests/test_map.py::CMapTest::test_map_in_1 tests/test_map.py::CMapTest::test_map_items_1 tests/test_map.py::CMapTest::test_map_items_2 tests/test_map.py::CMapTest::test_map_items_3 tests/test_map.py::CMapTest::test_map_items_4 tests/test_map.py::CMapTest::test_map_keys_1 tests/test_map.py::CMapTest::test_map_keys_2 tests/test_map.py::CMapTest::test_map_mut_1 tests/test_map.py::CMapTest::test_map_mut_10 tests/test_map.py::CMapTest::test_map_mut_11 tests/test_map.py::CMapTest::test_map_mut_12 tests/test_map.py::CMapTest::test_map_mut_13 tests/test_map.py::CMapTest::test_map_mut_14 tests/test_map.py::CMapTest::test_map_mut_15 tests/test_map.py::CMapTest::test_map_mut_16 tests/test_map.py::CMapTest::test_map_mut_17 tests/test_map.py::CMapTest::test_map_mut_18 tests/test_map.py::CMapTest::test_map_mut_19 tests/test_map.py::CMapTest::test_map_mut_2 tests/test_map.py::CMapTest::test_map_mut_3 tests/test_map.py::CMapTest::test_map_mut_4 tests/test_map.py::CMapTest::test_map_mut_5 tests/test_map.py::CMapTest::test_map_mut_6 tests/test_map.py::CMapTest::test_map_mut_7 tests/test_map.py::CMapTest::test_map_mut_8 tests/test_map.py::CMapTest::test_map_mut_9 tests/test_map.py::CMapTest::test_map_mut_stress tests/test_map.py::CMapTest::test_map_pickle tests/test_map.py::CMapTest::test_map_stress tests/test_map.py::CMapTest::test_map_values_1 tests/test_map.py::CMapTest::test_map_values_2 tests/test_map.py::CMapTest::test_repr_1 tests/test_map.py::CMapTest::test_repr_2 tests/test_map.py::CMapTest::test_repr_3
immutables/map.py immutables/_map.pyi immutables/py.typed immutables/_map.c
immutables/map.py immutables/_map.pyi immutables/py.typed immutables/_map.c