diff --git a/cloudpickle/cloudpickle.py b/cloudpickle/cloudpickle.py index 08882306..646f133e 100644 --- a/cloudpickle/cloudpickle.py +++ b/cloudpickle/cloudpickle.py @@ -595,18 +595,25 @@ class id will also reuse this enum definition. return _lookup_class_or_track(class_tracker_id, enum_class) -def _make_typevar(name, bound, constraints, covariant, contravariant, class_tracker_id): +def _make_typevar( + name, bound, constraints, covariant, contravariant, class_tracker_id, extra=None +): tv = typing.TypeVar( name, *constraints, bound=bound, covariant=covariant, contravariant=contravariant, + **(extra or {}), ) return _lookup_class_or_track(class_tracker_id, tv) def _decompose_typevar(obj): + extra = {} + for attr in ("infer_variance", "default"): + if hasattr(obj, f"__{attr}__"): + extra[attr] = getattr(obj, f"__{attr}__") return ( obj.__name__, obj.__bound__, @@ -614,6 +621,7 @@ def _decompose_typevar(obj): obj.__covariant__, obj.__contravariant__, _get_or_create_tracker_id(obj), + extra, ) diff --git a/tests/cloudpickle_test.py b/tests/cloudpickle_test.py index e2097d1c..47816a07 100644 --- a/tests/cloudpickle_test.py +++ b/tests/cloudpickle_test.py @@ -2576,6 +2576,18 @@ def test_pickle_dynamic_typevar_tracking(self): T2 = subprocess_pickle_echo(T, protocol=self.protocol) assert T is T2 + @pytest.mark.skipif(sys.version_info < (3, 12), reason="Needs PEP 695") + def test_pickle_dynamic_typevar_infer_variance(self): + T = typing.TypeVar("T", infer_variance=True) + with subprocess_worker(protocol=self.protocol) as worker: + assert worker.run(lambda t: t.__infer_variance__, T) is True + + @pytest.mark.skipif(sys.version_info < (3, 13), reason="Needs PEP 696") + def test_pickle_dynamic_typevar_default(self): + T = typing.TypeVar("T", default=int) + with subprocess_worker(protocol=self.protocol) as worker: + assert worker.run(lambda t: t.__default__, T) is int + def test_pickle_dynamic_typevar_memoization(self): T = typing.TypeVar("T") depickled_T1, depickled_T2 = pickle_depickle((T, T), protocol=self.protocol)