I'm trying to understand how type variance works with Python protocols and generics. My test cases seem to contradict what I expect regarding invariant, covariant, and contravariant behavior. Here's a minimal example demonstrating the issue: from typing import TypeVar, Protocol # Type variables T = TypeVar('T') T_co = TypeVar('T_co', covariant=True) T_contra = TypeVar('T_contra', contravariant=True) # Class hierarchy class Animal: pass class Dog(Animal): pass # Protocols class Feeder(Protocol[T]): def feed(self, animal: T) -> T: ... class Adopter(Protocol[T_co]): def adopt(self) -> T_co: ... class Walker(Protocol[T_contra]): def walk(self, animal: T_contra) -> None: ... # Implementations class AnimalFeeder: def feed(self, animal: Animal) -> Animal: ... class DogFeeder: def feed(self, animal: Dog) -> Dog: ... class AnimalAdopter: def adopt(self) -> Animal: ... class DogAdopter: def adopt(self) -> Dog: ... class AnimalWalker: def walk(self, animal: Animal) -> None: ... class DogWalker: def walk(self, animal: Dog) -> None: ...  When testing type assignments, some cases behave differently than expected: # Test cases with expected vs actual behavior feeder1: Feeder[Dog] = DogFeeder() # Expected ✅ Actual ✅ (exact match) feeder2: Feeder[Dog] = AnimalFeeder() # Expected ❌ Actual ❌ (invariant) feeder3: Feeder[Animal] = DogFeeder() # Expected ❌ Actual ✅ (Why does this work?) adopter1: Adopter[Dog] = DogAdopter() # Expected ✅ Actual ✅ (exact match) adopter2: Adopter[Dog] = AnimalAdopter() # Expected ❌ Actual ❌ (return type mismatch) adopter3: Adopter[Animal] = DogAdopter() # Expected ✅ Actual ✅ (covariant, correct) walker1: Walker[Dog] = DogWalker() # Expected ✅ Actual ✅ (exact match) walker2: Walker[Dog] = AnimalWalker() # Expected ✅ Actual ❌ (Should work with contravariance?) walker3: Walker[Animal] = DogWalker() # Expected ❌ Actual ✅ (Why does this work?)  Questions:  Why does feeder3 type-check when it seemingly violates invariance? Why does walker2 fail when it should be valid with contravariance? Are these behaviors correct according to Python's type system, or is this a limitation in PyCharm's type checking?  I'm using Python 3.10 and PyCharm 2024.3.1.
 feeder3 works because Python's structural typing checks method return type covariance (accepting Dog as Animal), but incorrectly ignores parameter contravariance (should reject Dog input for Animal parameter). This is a type checker limitation (PyCharm/mypy may differ).  walker2 fails due to insufficient contravariance support in some type checkers. AnimalWalker (handles supertype Animal) should satisfy Walker[Dog] (needs subtype input) via contravariance, but PyCharm doesn't recognize  Behaviors are partially incorrect - true variance rules (per PEP 544) would reject feeder3 and accept walker2. PyCharm's checker has limitations in enforcing variance for protocols, unlike stricter tools like mypy.  